Layer: Support layer 206 and 207 (#6038)
This commit is contained in:
parent
d76356da1f
commit
1e12d4f628
399
CLAUDE.md
Normal file
399
CLAUDE.md
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
# Instructions
|
||||||
|
|
||||||
|
You are an expert in TypeScript, JavaScript, HTML, SCSS and Teact with deep experience in our project's simplified React-like API. You are working on a modern web app for Telegram.
|
||||||
|
|
||||||
|
- **Be concise.** Only change code directly related to the current task; leave unrelated parts untouched.
|
||||||
|
- **Reuse** existing types, functions and components. Search before creating a new one.
|
||||||
|
- **No new libraries.** Use existing dependencies only. If a task truly can't be done without a new library, stop and explain why.
|
||||||
|
- **Do not** write tests.
|
||||||
|
|
||||||
|
- **SCSS modules:**
|
||||||
|
- Name classes in camelCase.
|
||||||
|
- Import as `styles` in your component:
|
||||||
|
```scss
|
||||||
|
/* Component.module.scss */
|
||||||
|
.myWrapper { /*…*/ }
|
||||||
|
```
|
||||||
|
```tsx
|
||||||
|
/* Component.tsx */
|
||||||
|
import styles from "./Component.module.scss";
|
||||||
|
<div className={buildClassName(styles.myWrapper, "legacy-class")} />
|
||||||
|
```
|
||||||
|
- Use [buildClassName.ts](mdc:src/util/buildClassName.ts) to merge multiple class names.
|
||||||
|
- **Always extract styles to files** - avoid inline styles unless absolutely necessary.
|
||||||
|
- **If file already imports styles**, check where they come from and add new styles there - don't create new style files.
|
||||||
|
- Use rem units for all measurements.
|
||||||
|
|
||||||
|
- **Code Style:**
|
||||||
|
- Early returns.
|
||||||
|
- Prefix boolean variables with primary or modal auxiliaries (e.q. `isOpen`, `willUpdate`, `shouldRender`).
|
||||||
|
- Functions should start with a verb (e.q. `openModal`, `closeDialog`, `handleClick`).
|
||||||
|
- Prefer checking required parameter before calling a function, avoid making it optinal and checking at the beginning of the function.
|
||||||
|
- Only leave comments for complex logic.
|
||||||
|
- **IMPORTANT: Avoid conditional spread operators** - TypeScript doesn't check if spread fields match the target type.
|
||||||
|
```typescript
|
||||||
|
// ❌ BAD - No type checking
|
||||||
|
{ ...condition && { field: value } }
|
||||||
|
|
||||||
|
// ✅ GOOD - Full type checking
|
||||||
|
{ field: condition ? value : undefined }
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Localization & Text Rules:**
|
||||||
|
- **ALWAYS** use `lang()` for all text content - never hardcode strings.
|
||||||
|
- `lang()` can accept parameters: `lang('Key', { param: value })`.
|
||||||
|
- Add new translations to end of `src/assets/localization/fallback.strings`.
|
||||||
|
|
||||||
|
- **After your solution:**
|
||||||
|
1. Critique it—identify any shortcomings.
|
||||||
|
2. Fix those issues, do more planning.
|
||||||
|
3. Present the improved result.
|
||||||
|
|
||||||
|
- **When deeper debugging is needed:**
|
||||||
|
1. Outline clear, step-by-step debugging instructions for the operator.
|
||||||
|
2. Remove any temporary debug code once the issue is resolved.
|
||||||
|
|
||||||
|
- **Lint errors you can't fix manually:**
|
||||||
|
Suggest running `eslint --fix <filename>`.
|
||||||
|
|
||||||
|
# Telegram Web API Guide
|
||||||
|
|
||||||
|
## 1. API Definition
|
||||||
|
- The master file is `src/lib/gramjs/tl/static/api.tl` (TL syntax).
|
||||||
|
- **Don't edit** this autogenerated file. TypeScript types live in `api.d.ts`.
|
||||||
|
- We use GramJS inside a web worker; UI code uses plain objects (`Api*` types) in `src/api/types`.
|
||||||
|
|
||||||
|
## 2. Generating Code
|
||||||
|
1. Make sure to include the method name in `api.json`.
|
||||||
|
2. Run:
|
||||||
|
```bash
|
||||||
|
npm run gramjs:tl
|
||||||
|
```
|
||||||
|
to regenerate `api.d.ts`.
|
||||||
|
3. In `src/api/gramjs/methods/`, pick a file for your method, then:
|
||||||
|
* Name fetchers `fetch*` if the TL method starts with `get`.
|
||||||
|
* Use a destructured parameter object.
|
||||||
|
* Call the API via:
|
||||||
|
```ts
|
||||||
|
const result = await invokeRequest(
|
||||||
|
new GramJs.namespace.MethodName({ /* params */ })
|
||||||
|
);
|
||||||
|
```
|
||||||
|
* If `result` is `undefined`, return `undefined` to signal an error.
|
||||||
|
* Convert any returned GramJS classes into plain `Api*` objects.
|
||||||
|
|
||||||
|
Convesion from and to Api* objects is done by `apiBuilders` (function name starts with `buildApi*`) and `gramjsBuilders` (function name `buildInput*`).
|
||||||
|
|
||||||
|
## 3. Using the API
|
||||||
|
|
||||||
|
* In your actions, call:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const result = await callApi('methodName', { /* params */ });
|
||||||
|
```
|
||||||
|
* Always check for `undefined` before proceeding.
|
||||||
|
|
||||||
|
## 4. Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// src/api/gramjs/methods/users.ts
|
||||||
|
export async function fetchUsers({ users }: { users: ApiUser[] }) {
|
||||||
|
const result = await invokeRequest(new GramJs.users.GetUsers({
|
||||||
|
id: users.map(({ id, accessHash }) => buildInputUser(id, accessHash)),
|
||||||
|
}));
|
||||||
|
if (!result || !result.length) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiUsers = result.map(buildApiUser).filter(Boolean);
|
||||||
|
const userStatusesById = buildApiUserStatuses(result);
|
||||||
|
|
||||||
|
return {
|
||||||
|
users: apiUsers,
|
||||||
|
userStatusesById,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/global/actions/api/users.ts
|
||||||
|
addActionHandler('loadUser', async (global, actions, { userId }) => {
|
||||||
|
const user = selectUser(global, userId);
|
||||||
|
if (!user) return;
|
||||||
|
const res = await callApi('fetchUsers', { users: [user] });
|
||||||
|
if (!res) return;
|
||||||
|
// update global state...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Handling Updates
|
||||||
|
|
||||||
|
* Updates come in via `mtpUpdateHandler.ts`.
|
||||||
|
* They're routed through `src/global/actions/apiUpdaters` to merge into global state.
|
||||||
|
* Types are defined in `src/api/types/updates.ts`.
|
||||||
|
|
||||||
|
## Component Style Guide
|
||||||
|
|
||||||
|
### 1. Basics & Imports
|
||||||
|
|
||||||
|
* All components use JSX and render with Teact.
|
||||||
|
* **Always** import React from teact library, for JSX compatibility reasons. Only import from `'react'` when you need React **types** that are not provided in Teact.
|
||||||
|
* Built-in hooks live in `src/lib/teact/teact`. Import them from there.
|
||||||
|
|
||||||
|
### 2. Props & Types
|
||||||
|
|
||||||
|
* Split your props into two types:
|
||||||
|
* **OwnProps**: data passed in by the parent
|
||||||
|
* **StateProps**: data injected by `withGlobal` HOC
|
||||||
|
* Merge them as `OwnProps & StateProps` when defining your component.
|
||||||
|
* You can skip one or both if they are not used.
|
||||||
|
* **Order rule**: list any function types *last* in your props definitions.
|
||||||
|
|
||||||
|
### 3. Hooks
|
||||||
|
* **useLastCallback** is your go-to for callbacks, since it won't trigger re-renders and always uses the latest scope.
|
||||||
|
* Only use **useCallback** when you really need to memoize a render function.
|
||||||
|
* Prefer **useFlag()** over `useState<boolean>()` for simple boolean toggles.
|
||||||
|
* Check the `hooks/` folders for additional utilities.
|
||||||
|
|
||||||
|
### 4. Component Signature
|
||||||
|
> **Migrate** any old `FC` syntax to the new form.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Before
|
||||||
|
const OldComp: FC<OwnProps & StateProps> = ({ … }) => { … }
|
||||||
|
|
||||||
|
// After
|
||||||
|
const NewComp = (props: OwnProps & StateProps) => { … }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Memoization
|
||||||
|
* Wrap most components with `memo()` to avoid unnecessary updates.
|
||||||
|
* Don't pass freshly created objects or arrays as props to memoized components.
|
||||||
|
* **Exceptions** (no memo): `ListItem`, `Button`, `MenuItem`, etc.
|
||||||
|
|
||||||
|
### 6. Localization
|
||||||
|
|
||||||
|
* Call `const lang = useLang()` at the top of your component.
|
||||||
|
* Look up the localization guide for how to add new language keys.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import React, { useFlag } from '../../lib/teact/teact';
|
||||||
|
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import styles from './Component.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
id: string;
|
||||||
|
className?: string;
|
||||||
|
onClick?: NoneToVoidFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
stateValue?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constants first
|
||||||
|
const MAX_ITEMS = 10
|
||||||
|
|
||||||
|
const Component = ({ id, className, stateValue, onClick }: OwnProps & StateProps) => {
|
||||||
|
const { someAction } = getActions(); // Should always be first, if actions are used
|
||||||
|
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const [color, setColor] = useState('#FF00FF');
|
||||||
|
const [isOpen, open, close] = useFlag();
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const handleClick = useLastCallback(() => {
|
||||||
|
if (!ref.current) return;
|
||||||
|
const el = ref.current;
|
||||||
|
setColor(el.value);
|
||||||
|
close();
|
||||||
|
onClick?.();
|
||||||
|
someAction(el.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={ref} className={styles.root + (className ? ` ${className}` : '')}>
|
||||||
|
<button onClick={handleClick}>{lang('ButtonKey')}</button>
|
||||||
|
<p>{stateValue}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>((global, { id }): StateProps => {
|
||||||
|
|
||||||
|
const stateValue = selectValue(global, id);
|
||||||
|
return {
|
||||||
|
stateValue,
|
||||||
|
};
|
||||||
|
})(Component);
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Global State Overview
|
||||||
|
|
||||||
|
Global State is our single, app-wide store, similar to Redux or Zustand. All its code lives under `src/global/`, with subfolders grouping related functionality (for example, `selectors/users.ts` holds all user-related selectors).
|
||||||
|
|
||||||
|
### 1. Folder Structure
|
||||||
|
|
||||||
|
* **`actions/`**: Actions that are used to update global from any point in the app
|
||||||
|
* **`selectors/`**: Pure functions that read data (e.g. `selectors/users.ts`).
|
||||||
|
* **`reducers/`**: Functions that update global state.
|
||||||
|
* **`types/`**: All TypeScript types live in `src/global/types`.
|
||||||
|
* **`cache.ts`**: Manages saving a slimmed-down copy of global to IndexedDB.
|
||||||
|
|
||||||
|
### 2. Actions
|
||||||
|
|
||||||
|
1. **Preffered** way to update global. When inside action, use `setGlobal`, or simple `return` if sync.
|
||||||
|
2. **Sync actions** return type should be `ActionReturnType`.
|
||||||
|
3. **Async actions** return type should be `Promise<void>`.
|
||||||
|
4. If you add or remove an action, update `actions.ts` accordingly.
|
||||||
|
5. Actions in `ui` folder should be only sync.
|
||||||
|
|
||||||
|
### 3. Multi-Tab Support
|
||||||
|
|
||||||
|
* Actions and selectors can accept a `tabId` parameter, so we don't lose tab context when working with multiple tabs.
|
||||||
|
* **`tabId` is required** if calling an action or selector that can accept it.
|
||||||
|
* **Exception**: UI components may call without `tabId` (they receive it automatically).
|
||||||
|
|
||||||
|
### 4. Selectors & Reducers
|
||||||
|
|
||||||
|
* If logic takes more than one line, create a new selector or reducer in the appropriate folder and file.
|
||||||
|
* **Selectors must be pure**: only use their inputs and global. Don't allocate new objects or arrays, as that breaks memoization.
|
||||||
|
|
||||||
|
### 5. Data Constraints
|
||||||
|
|
||||||
|
* Global may only store serializable primitives (strings, numbers, booleans).
|
||||||
|
* When you change a type that's cached in `cache.ts`, add a migration to avoid errors from new selectors.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Component Guidelines
|
||||||
|
|
||||||
|
### 1. Accessing Global in Components
|
||||||
|
|
||||||
|
* **Use** `withGlobal` (a `mapStateToProps` helper) to pull in state.
|
||||||
|
* **Avoid** the experimental `useSelector` API.
|
||||||
|
* **Use** `getGlobal` **only** inside hooks for one-off reads (it's non-reactive).
|
||||||
|
|
||||||
|
### 2. Performance
|
||||||
|
|
||||||
|
* Wrap `withGlobal` in `memo` so the component re-renders only on real data changes.
|
||||||
|
* **Don't** return new arrays or objects inside `withGlobal`; that defeats memoization.
|
||||||
|
* If you need to filter or map a list, **pass IDs as props** and do the heavy work in a `useMemo` hook.
|
||||||
|
|
||||||
|
### 3. Example Component
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type OwnProps = { id: string };
|
||||||
|
type StateProps = {
|
||||||
|
someValue?: string;
|
||||||
|
otherValue?: number;
|
||||||
|
thirdValue: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Component = ({
|
||||||
|
id,
|
||||||
|
someValue,
|
||||||
|
otherValue,
|
||||||
|
thirdValue,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
|
// component logic...
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(
|
||||||
|
withGlobal<OwnProps>((global, { id }) => {
|
||||||
|
const { otherValue } = selectTabState(global);
|
||||||
|
const someValue = selectSomeValue(global, id);
|
||||||
|
const thirdValue = Boolean(global.rawValue);
|
||||||
|
|
||||||
|
return {
|
||||||
|
someValue,
|
||||||
|
otherValue,
|
||||||
|
thirdValue,
|
||||||
|
};
|
||||||
|
})(Component);
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
# Localization Guide
|
||||||
|
|
||||||
|
**1. Setup & Fallback**
|
||||||
|
|
||||||
|
* Translations live on [Translation Platform](https://translations.telegram.org/).
|
||||||
|
* Fallback file: `src/assets/localization/fallback.strings`.
|
||||||
|
|
||||||
|
**2. Getting Strings**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
// Simple
|
||||||
|
lang('SimpleKey');
|
||||||
|
|
||||||
|
// Plurals
|
||||||
|
lang('PluralKey', undefined, { pluralValue: 3 });
|
||||||
|
|
||||||
|
// String replacements
|
||||||
|
lang('ReplKey', { name: 'Amy' });
|
||||||
|
|
||||||
|
// JSX nodes (e.g. links)
|
||||||
|
lang('LinkKey', { link: <Link /> }, { withNodes: true });
|
||||||
|
|
||||||
|
// Markdown
|
||||||
|
lang('MarkdownKey', undefined, { withNodes: true, withMarkdown: true });
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Adding a New Key**
|
||||||
|
|
||||||
|
1. Search Translation Platform for similar strings to get the correct wording.
|
||||||
|
2. Add it to `fallback.strings`.
|
||||||
|
3. If it's plural, include `_one` and `_other`.
|
||||||
|
4. Run `npm run lang:ts`.
|
||||||
|
|
||||||
|
**4. Naming Rules**
|
||||||
|
|
||||||
|
* **PascalCase** (no dots).
|
||||||
|
* Use short, clear prefixes for context (e.g. `Acc` for accessibility).
|
||||||
|
* Keep names under ~30 chars, shorten consistently if needed.
|
||||||
|
|
||||||
|
**5. API & Options**
|
||||||
|
|
||||||
|
* **Basic**: `lang(key, vars?, options?) → string`
|
||||||
|
|
||||||
|
* **Advanced** (`withNodes`): returns `TeactNode[]` so you can inject JSX.
|
||||||
|
|
||||||
|
* **Other options**:
|
||||||
|
|
||||||
|
* `withMarkdown` (for simple markdown + emojis)
|
||||||
|
* `renderTextFilters` (custom filters)
|
||||||
|
* `specialReplacement` (for replacing substrings, e.g. icons)
|
||||||
|
|
||||||
|
* **Object syntax**:
|
||||||
|
Simple form that returns string can be used in some actions.
|
||||||
|
```ts
|
||||||
|
actions.showNotification({ key: 'LangKey' });
|
||||||
|
|
||||||
|
lang.with({ key: 'hello', vars: { name }, options: { withNodes: true } });
|
||||||
|
```
|
||||||
|
|
||||||
|
**6. Handy Extensions**
|
||||||
|
|
||||||
|
* `lang.region(code)` → country name
|
||||||
|
* `lang.conjunction(['a','b','c'])` → "a, b, and c"
|
||||||
|
* `lang.disjunction(['x','y'])` → "x or y"
|
||||||
|
* `lang.number(1234)` → locale-formatted number
|
||||||
|
* Flags: `lang.isRtl`, `lang.code`, `lang.rawCode`
|
||||||
|
|
||||||
|
**7. Beyond React**
|
||||||
|
Use `getTranslationFn()` to grab the same `lang` function in non-component code. Discouraged, use object syntax.
|
||||||
@ -101,6 +101,13 @@ export interface GramJsAppConfig extends LimitsConfig {
|
|||||||
stars_stargift_resale_amount_max?: number;
|
stars_stargift_resale_amount_max?: number;
|
||||||
stars_stargift_resale_amount_min?: number;
|
stars_stargift_resale_amount_min?: number;
|
||||||
stars_stargift_resale_commission_permille?: number;
|
stars_stargift_resale_commission_permille?: number;
|
||||||
|
stars_suggested_post_amount_max?: number;
|
||||||
|
stars_suggested_post_amount_min?: number;
|
||||||
|
stars_suggested_post_commission_permille?: number;
|
||||||
|
stars_suggested_post_age_min?: number;
|
||||||
|
stars_suggested_post_future_max?: number;
|
||||||
|
stars_suggested_post_future_min?: number;
|
||||||
|
ton_suggested_post_commission_permille?: number;
|
||||||
poll_answers_max?: number;
|
poll_answers_max?: number;
|
||||||
todo_items_max?: number;
|
todo_items_max?: number;
|
||||||
todo_title_length_max?: number;
|
todo_title_length_max?: number;
|
||||||
@ -205,6 +212,13 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
|
|||||||
starsStargiftResaleAmountMin: appConfig.stars_stargift_resale_amount_min,
|
starsStargiftResaleAmountMin: appConfig.stars_stargift_resale_amount_min,
|
||||||
starsStargiftResaleAmountMax: appConfig.stars_stargift_resale_amount_max,
|
starsStargiftResaleAmountMax: appConfig.stars_stargift_resale_amount_max,
|
||||||
starsStargiftResaleCommissionPermille: appConfig.stars_stargift_resale_commission_permille,
|
starsStargiftResaleCommissionPermille: appConfig.stars_stargift_resale_commission_permille,
|
||||||
|
starsSuggestedPostAmountMax: appConfig.stars_suggested_post_amount_max,
|
||||||
|
starsSuggestedPostAmountMin: appConfig.stars_suggested_post_amount_min,
|
||||||
|
starsSuggestedPostCommissionPermille: appConfig.stars_suggested_post_commission_permille,
|
||||||
|
starsSuggestedPostAgeMin: appConfig.stars_suggested_post_age_min,
|
||||||
|
starsSuggestedPostFutureMax: appConfig.stars_suggested_post_future_max,
|
||||||
|
starsSuggestedPostFutureMin: appConfig.stars_suggested_post_future_min,
|
||||||
|
tonSuggestedPostCommissionPermille: appConfig.ton_suggested_post_commission_permille,
|
||||||
pollMaxAnswers: appConfig.poll_answers_max,
|
pollMaxAnswers: appConfig.poll_answers_max,
|
||||||
todoItemsMax: appConfig.todo_items_max ?? TODO_ITEMS_LIMIT,
|
todoItemsMax: appConfig.todo_items_max ?? TODO_ITEMS_LIMIT,
|
||||||
todoTitleLengthMax: appConfig.todo_title_length_max ?? TODO_TITLE_LENGTH_LIMIT,
|
todoTitleLengthMax: appConfig.todo_title_length_max ?? TODO_TITLE_LENGTH_LIMIT,
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
|
|||||||
if (starGift instanceof GramJs.StarGiftUnique) {
|
if (starGift instanceof GramJs.StarGiftUnique) {
|
||||||
const {
|
const {
|
||||||
id, num, ownerId, ownerName, title, attributes, availabilityIssued, availabilityTotal, slug, ownerAddress,
|
id, num, ownerId, ownerName, title, attributes, availabilityIssued, availabilityTotal, slug, ownerAddress,
|
||||||
giftAddress, resellStars,
|
giftAddress, resellStars, releasedBy,
|
||||||
} = starGift;
|
} = starGift;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -41,12 +41,13 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
|
|||||||
slug,
|
slug,
|
||||||
giftAddress,
|
giftAddress,
|
||||||
resellPriceInStars: resellStars?.toJSNumber(),
|
resellPriceInStars: resellStars?.toJSNumber(),
|
||||||
|
releasedByPeerId: releasedBy && getApiChatIdFromMtpPeer(releasedBy),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
id, limited, stars, availabilityRemains, availabilityTotal, convertStars, firstSaleDate, lastSaleDate, soldOut,
|
id, limited, stars, availabilityRemains, availabilityTotal, convertStars, firstSaleDate, lastSaleDate, soldOut,
|
||||||
birthday, upgradeStars, resellMinStars, title, availabilityResale,
|
birthday, upgradeStars, resellMinStars, title, availabilityResale, releasedBy,
|
||||||
} = starGift;
|
} = starGift;
|
||||||
|
|
||||||
addDocumentToLocalDb(starGift.sticker);
|
addDocumentToLocalDb(starGift.sticker);
|
||||||
@ -69,6 +70,7 @@ export function buildApiStarGift(starGift: GramJs.TypeStarGift): ApiStarGift {
|
|||||||
upgradeStars: upgradeStars?.toJSNumber(),
|
upgradeStars: upgradeStars?.toJSNumber(),
|
||||||
title,
|
title,
|
||||||
resellMinStars: resellMinStars?.toJSNumber(),
|
resellMinStars: resellMinStars?.toJSNumber(),
|
||||||
|
releasedByPeerId: releasedBy && getApiChatIdFromMtpPeer(releasedBy),
|
||||||
availabilityResale: availabilityResale?.toJSNumber(),
|
availabilityResale: availabilityResale?.toJSNumber(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { buildApiBotApp } from './bots';
|
|||||||
import { buildApiFormattedText, buildApiPhoto } from './common';
|
import { buildApiFormattedText, buildApiPhoto } from './common';
|
||||||
import { buildApiStarGift } from './gifts';
|
import { buildApiStarGift } from './gifts';
|
||||||
import { buildTodoItem } from './messageContent';
|
import { buildTodoItem } from './messageContent';
|
||||||
|
import { buildApiStarsAmount } from './payments';
|
||||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
||||||
|
|
||||||
const UNSUPPORTED_ACTION: ApiMessageAction = {
|
const UNSUPPORTED_ACTION: ApiMessageAction = {
|
||||||
@ -447,6 +448,36 @@ export function buildApiMessageAction(action: GramJs.TypeMessageAction): ApiMess
|
|||||||
count,
|
count,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (action instanceof GramJs.MessageActionSuggestedPostApproval) {
|
||||||
|
const {
|
||||||
|
rejected, balanceTooLow, rejectComment, scheduleDate, price,
|
||||||
|
} = action;
|
||||||
|
return {
|
||||||
|
mediaType: 'action',
|
||||||
|
type: 'suggestedPostApproval',
|
||||||
|
isRejected: Boolean(rejected),
|
||||||
|
isBalanceTooLow: Boolean(balanceTooLow),
|
||||||
|
rejectComment,
|
||||||
|
scheduleDate,
|
||||||
|
amount: price ? buildApiStarsAmount(price) : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action instanceof GramJs.MessageActionSuggestedPostSuccess) {
|
||||||
|
const { price } = action;
|
||||||
|
return {
|
||||||
|
mediaType: 'action',
|
||||||
|
type: 'suggestedPostSuccess',
|
||||||
|
amount: buildApiStarsAmount(price),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action instanceof GramJs.MessageActionSuggestedPostRefund) {
|
||||||
|
const { payerInitiated } = action;
|
||||||
|
return {
|
||||||
|
mediaType: 'action',
|
||||||
|
type: 'suggestedPostRefund',
|
||||||
|
payerInitiated: Boolean(payerInitiated),
|
||||||
|
};
|
||||||
|
}
|
||||||
if (action instanceof GramJs.MessageActionTodoCompletions) {
|
if (action instanceof GramJs.MessageActionTodoCompletions) {
|
||||||
const {
|
const {
|
||||||
completed, incompleted,
|
completed, incompleted,
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import type {
|
|||||||
ApiFactCheck,
|
ApiFactCheck,
|
||||||
ApiInputMessageReplyInfo,
|
ApiInputMessageReplyInfo,
|
||||||
ApiInputReplyInfo,
|
ApiInputReplyInfo,
|
||||||
|
ApiInputSuggestedPostInfo,
|
||||||
ApiMediaTodo,
|
ApiMediaTodo,
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
@ -25,6 +26,7 @@ import type {
|
|||||||
ApiSticker,
|
ApiSticker,
|
||||||
ApiStory,
|
ApiStory,
|
||||||
ApiStorySkipped,
|
ApiStorySkipped,
|
||||||
|
ApiSuggestedPost,
|
||||||
ApiThreadInfo,
|
ApiThreadInfo,
|
||||||
ApiVideo,
|
ApiVideo,
|
||||||
MediaContent,
|
MediaContent,
|
||||||
@ -44,6 +46,9 @@ import { addTimestampEntities } from '../../../util/dates/timestamp';
|
|||||||
import { omitUndefined, pick } from '../../../util/iteratees';
|
import { omitUndefined, pick } from '../../../util/iteratees';
|
||||||
import { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
|
import { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
|
||||||
import { interpolateArray } from '../../../util/waveform';
|
import { interpolateArray } from '../../../util/waveform';
|
||||||
|
import {
|
||||||
|
buildApiStarsAmount,
|
||||||
|
} from '../apiBuilders/payments';
|
||||||
import { buildPeer } from '../gramjsBuilders';
|
import { buildPeer } from '../gramjsBuilders';
|
||||||
import {
|
import {
|
||||||
addDocumentToLocalDb,
|
addDocumentToLocalDb,
|
||||||
@ -241,6 +246,7 @@ export function buildApiMessageWithChatId(
|
|||||||
reactions: mtpMessage.reactions && buildMessageReactions(mtpMessage.reactions),
|
reactions: mtpMessage.reactions && buildMessageReactions(mtpMessage.reactions),
|
||||||
emojiOnlyCount,
|
emojiOnlyCount,
|
||||||
...(mtpMessage.replyTo && { replyInfo: buildApiReplyInfo(mtpMessage.replyTo, mtpMessage) }),
|
...(mtpMessage.replyTo && { replyInfo: buildApiReplyInfo(mtpMessage.replyTo, mtpMessage) }),
|
||||||
|
...(mtpMessage.suggestedPost && { suggestedPostInfo: buildApiSuggestedPost(mtpMessage.suggestedPost) }),
|
||||||
forwardInfo,
|
forwardInfo,
|
||||||
isEdited,
|
isEdited,
|
||||||
editDate: mtpMessage.editDate,
|
editDate: mtpMessage.editDate,
|
||||||
@ -280,7 +286,7 @@ export function buildMessageDraft(draft: GramJs.TypeDraftMessage): ApiDraft | un
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
message, entities, replyTo, date, effect,
|
message, entities, replyTo, date, effect, suggestedPost,
|
||||||
} = draft;
|
} = draft;
|
||||||
|
|
||||||
const replyInfo = replyTo instanceof GramJs.InputReplyToMessage ? {
|
const replyInfo = replyTo instanceof GramJs.InputReplyToMessage ? {
|
||||||
@ -293,14 +299,31 @@ export function buildMessageDraft(draft: GramJs.TypeDraftMessage): ApiDraft | un
|
|||||||
quoteOffset: replyTo.quoteOffset,
|
quoteOffset: replyTo.quoteOffset,
|
||||||
} satisfies ApiInputMessageReplyInfo : undefined;
|
} satisfies ApiInputMessageReplyInfo : undefined;
|
||||||
|
|
||||||
|
const suggestedPostInfo = suggestedPost instanceof GramJs.SuggestedPost ? {
|
||||||
|
isAccepted: suggestedPost.accepted,
|
||||||
|
isRejected: suggestedPost.rejected,
|
||||||
|
price: suggestedPost.price ? buildApiStarsAmount(suggestedPost.price) : undefined,
|
||||||
|
scheduleDate: suggestedPost.scheduleDate,
|
||||||
|
} satisfies ApiInputSuggestedPostInfo : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
text: message ? buildMessageTextContent(message, entities) : undefined,
|
text: message ? buildMessageTextContent(message, entities) : undefined,
|
||||||
replyInfo,
|
replyInfo,
|
||||||
|
suggestedPostInfo,
|
||||||
date,
|
date,
|
||||||
effectId: effect?.toString(),
|
effectId: effect?.toString(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildApiSuggestedPost(suggestedPost: GramJs.SuggestedPost): ApiSuggestedPost {
|
||||||
|
return {
|
||||||
|
isAccepted: suggestedPost.accepted,
|
||||||
|
isRejected: suggestedPost.rejected,
|
||||||
|
price: suggestedPost.price ? buildApiStarsAmount(suggestedPost.price) : undefined,
|
||||||
|
scheduleDate: suggestedPost.scheduleDate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function buildApiMessageForwardInfo(fwdFrom: GramJs.MessageFwdHeader, isChatWithSelf = false): ApiMessageForwardInfo {
|
function buildApiMessageForwardInfo(fwdFrom: GramJs.MessageFwdHeader, isChatWithSelf = false): ApiMessageForwardInfo {
|
||||||
const savedFromPeerId = fwdFrom.savedFromPeer && getApiChatIdFromMtpPeer(fwdFrom.savedFromPeer);
|
const savedFromPeerId = fwdFrom.savedFromPeer && getApiChatIdFromMtpPeer(fwdFrom.savedFromPeer);
|
||||||
const fromId = fwdFrom.fromId && getApiChatIdFromMtpPeer(fwdFrom.fromId);
|
const fromId = fwdFrom.fromId && getApiChatIdFromMtpPeer(fwdFrom.fromId);
|
||||||
@ -396,6 +419,7 @@ export function buildLocalMessage(
|
|||||||
text?: string,
|
text?: string,
|
||||||
entities?: ApiMessageEntity[],
|
entities?: ApiMessageEntity[],
|
||||||
replyInfo?: ApiInputReplyInfo,
|
replyInfo?: ApiInputReplyInfo,
|
||||||
|
suggestedPostInfo?: ApiInputSuggestedPostInfo,
|
||||||
attachment?: ApiAttachment,
|
attachment?: ApiAttachment,
|
||||||
sticker?: ApiSticker,
|
sticker?: ApiSticker,
|
||||||
gif?: ApiVideo,
|
gif?: ApiVideo,
|
||||||
@ -439,6 +463,7 @@ export function buildLocalMessage(
|
|||||||
isOutgoing: !isChannel,
|
isOutgoing: !isChannel,
|
||||||
senderId: chat.type !== 'chatTypePrivate' ? (sendAs?.id || currentUserId) : undefined,
|
senderId: chat.type !== 'chatTypePrivate' ? (sendAs?.id || currentUserId) : undefined,
|
||||||
replyInfo: resultReplyInfo,
|
replyInfo: resultReplyInfo,
|
||||||
|
suggestedPostInfo,
|
||||||
...(groupedId && {
|
...(groupedId && {
|
||||||
groupedId,
|
groupedId,
|
||||||
...(media && (media.photo || media.video) && { isInAlbum: true }),
|
...(media && (media.photo || media.video) && { isInAlbum: true }),
|
||||||
|
|||||||
@ -461,11 +461,26 @@ export function buildApiStarsGiftOptions(option: GramJs.StarsGiftOption): ApiSta
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildApiStarsAmount(amount: GramJs.StarsAmount): ApiStarsAmount {
|
export function buildApiStarsAmount(amount: GramJs.TypeStarsAmount): ApiStarsAmount | undefined {
|
||||||
return {
|
if (amount instanceof GramJs.StarsAmount) {
|
||||||
amount: amount.amount.toJSNumber(),
|
return {
|
||||||
|
amount: amount.amount.toJSNumber(),
|
||||||
|
nanos: amount.nanos,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amount instanceof GramJs.StarsTonAmount) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildInputStarsAmount(amount: ApiStarsAmount): GramJs.TypeStarsAmount {
|
||||||
|
return new GramJs.StarsAmount({
|
||||||
|
amount: bigInt(amount.amount),
|
||||||
nanos: amount.nanos,
|
nanos: amount.nanos,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildApiStarsGiveawayWinnersOption(
|
export function buildApiStarsGiveawayWinnersOption(
|
||||||
@ -532,9 +547,9 @@ export function buildApiStarsTransactionPeer(peer: GramJs.TypeStarsTransactionPe
|
|||||||
return { type: 'unsupported' };
|
return { type: 'unsupported' };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction): ApiStarsTransaction {
|
export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction): ApiStarsTransaction | undefined {
|
||||||
const {
|
const {
|
||||||
date, id, peer, stars, description, photo, title, refund, extendedMedia, failed, msgId, pending, gift, reaction,
|
date, id, peer, amount, description, photo, title, refund, extendedMedia, failed, msgId, pending, gift, reaction,
|
||||||
subscriptionPeriod, stargift, giveawayPostId, starrefCommissionPermille, stargiftUpgrade, paidMessages,
|
subscriptionPeriod, stargift, giveawayPostId, starrefCommissionPermille, stargiftUpgrade, paidMessages,
|
||||||
stargiftResale,
|
stargiftResale,
|
||||||
} = transaction;
|
} = transaction;
|
||||||
@ -548,11 +563,16 @@ export function buildApiStarsTransaction(transaction: GramJs.StarsTransaction):
|
|||||||
|
|
||||||
const starRefCommision = starrefCommissionPermille ? starrefCommissionPermille / 10 : undefined;
|
const starRefCommision = starrefCommissionPermille ? starrefCommissionPermille / 10 : undefined;
|
||||||
|
|
||||||
|
const starsAmount = buildApiStarsAmount(amount);
|
||||||
|
if (!starsAmount) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
date,
|
date,
|
||||||
peer: buildApiStarsTransactionPeer(peer),
|
peer: buildApiStarsTransactionPeer(peer),
|
||||||
stars: buildApiStarsAmount(stars),
|
stars: starsAmount,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
photo: photo && buildApiWebDocument(photo),
|
photo: photo && buildApiWebDocument(photo),
|
||||||
|
|||||||
@ -54,15 +54,15 @@ export function buildChannelStatistics(stats: GramJs.stats.BroadcastStats): ApiC
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function buildChannelMonetizationStatistics(
|
export function buildChannelMonetizationStatistics(
|
||||||
stats: GramJs.stats.BroadcastRevenueStats,
|
stats: GramJs.payments.StarsRevenueStats,
|
||||||
): ApiChannelMonetizationStatistics {
|
): ApiChannelMonetizationStatistics {
|
||||||
return {
|
return {
|
||||||
// Graphs
|
// Graphs
|
||||||
topHoursGraph: buildGraph(stats.topHoursGraph),
|
topHoursGraph: stats.topHoursGraph ? buildGraph(stats.topHoursGraph) : undefined,
|
||||||
revenueGraph: buildGraph(stats.revenueGraph, undefined, true, stats.usdRate),
|
revenueGraph: buildGraph(stats.revenueGraph, undefined, true, stats.usdRate),
|
||||||
|
|
||||||
// Statistics overview
|
// Statistics overview
|
||||||
balances: buildChannelMonetizationBalances(stats.balances),
|
balances: buildChannelMonetizationBalances(stats.status),
|
||||||
usdRate: stats.usdRate,
|
usdRate: stats.usdRate,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -269,7 +269,7 @@ function buildChannelMonetizationBalances({
|
|||||||
availableBalance,
|
availableBalance,
|
||||||
overallRevenue,
|
overallRevenue,
|
||||||
withdrawalEnabled,
|
withdrawalEnabled,
|
||||||
}: GramJs.BroadcastRevenueBalances): ChannelMonetizationBalances {
|
}: GramJs.StarsRevenueStatus): ChannelMonetizationBalances {
|
||||||
return {
|
return {
|
||||||
currentBalance: Number(currentBalance) / DECIMALS,
|
currentBalance: Number(currentBalance) / DECIMALS,
|
||||||
availableBalance: Number(availableBalance) / DECIMALS,
|
availableBalance: Number(availableBalance) / DECIMALS,
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import type {
|
|||||||
ApiInputPrivacyRules,
|
ApiInputPrivacyRules,
|
||||||
ApiInputReplyInfo,
|
ApiInputReplyInfo,
|
||||||
ApiInputStorePaymentPurpose,
|
ApiInputStorePaymentPurpose,
|
||||||
|
ApiInputSuggestedPostInfo,
|
||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
ApiNewMediaTodo,
|
ApiNewMediaTodo,
|
||||||
ApiNewPoll,
|
ApiNewPoll,
|
||||||
@ -40,6 +41,7 @@ import {
|
|||||||
|
|
||||||
import { CHANNEL_ID_BASE, DEFAULT_STATUS_ICON_ID } from '../../../config';
|
import { CHANNEL_ID_BASE, DEFAULT_STATUS_ICON_ID } from '../../../config';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
|
import { buildInputStarsAmount } from '../apiBuilders/payments';
|
||||||
import { deserializeBytes } from '../helpers/misc';
|
import { deserializeBytes } from '../helpers/misc';
|
||||||
import localDb from '../localDb';
|
import localDb from '../localDb';
|
||||||
|
|
||||||
@ -888,6 +890,16 @@ export function buildInputReplyTo(replyInfo: ApiInputReplyInfo) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildInputSuggestedPost(suggestedPostInfo: ApiInputSuggestedPostInfo): GramJs.SuggestedPost {
|
||||||
|
const isPaid = Boolean(suggestedPostInfo.price)
|
||||||
|
&& Boolean((suggestedPostInfo.price.amount || suggestedPostInfo.price.nanos));
|
||||||
|
|
||||||
|
return new GramJs.SuggestedPost({
|
||||||
|
price: isPaid ? buildInputStarsAmount(suggestedPostInfo.price!) : undefined,
|
||||||
|
scheduleDate: suggestedPostInfo.scheduleDate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function buildInputPrivacyRules(
|
export function buildInputPrivacyRules(
|
||||||
rules: ApiInputPrivacyRules,
|
rules: ApiInputPrivacyRules,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -68,6 +68,7 @@ import {
|
|||||||
buildInputPeer,
|
buildInputPeer,
|
||||||
buildInputPhoto,
|
buildInputPhoto,
|
||||||
buildInputReplyTo,
|
buildInputReplyTo,
|
||||||
|
buildInputSuggestedPost,
|
||||||
buildInputUser,
|
buildInputUser,
|
||||||
buildMtpMessageEntity,
|
buildMtpMessageEntity,
|
||||||
DEFAULT_PRIMITIVES,
|
DEFAULT_PRIMITIVES,
|
||||||
@ -526,6 +527,7 @@ export function saveDraft({
|
|||||||
message: draft?.text?.text || DEFAULT_PRIMITIVES.STRING,
|
message: draft?.text?.text || DEFAULT_PRIMITIVES.STRING,
|
||||||
entities: draft?.text?.entities?.map(buildMtpMessageEntity),
|
entities: draft?.text?.entities?.map(buildMtpMessageEntity),
|
||||||
replyTo: draft?.replyInfo && buildInputReplyTo(draft.replyInfo),
|
replyTo: draft?.replyInfo && buildInputReplyTo(draft.replyInfo),
|
||||||
|
suggestedPost: draft?.suggestedPostInfo && buildInputSuggestedPost(draft.suggestedPostInfo),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import type {
|
|||||||
ApiFormattedText,
|
ApiFormattedText,
|
||||||
ApiGlobalMessageSearchType,
|
ApiGlobalMessageSearchType,
|
||||||
ApiInputReplyInfo,
|
ApiInputReplyInfo,
|
||||||
|
ApiInputSuggestedPostInfo,
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
ApiMessageSearchContext,
|
ApiMessageSearchContext,
|
||||||
@ -76,13 +77,16 @@ import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
|||||||
import { buildApiUser, buildApiUserStatuses } from '../apiBuilders/users';
|
import { buildApiUser, buildApiUserStatuses } from '../apiBuilders/users';
|
||||||
import {
|
import {
|
||||||
buildInputChannel,
|
buildInputChannel,
|
||||||
|
buildInputDocument,
|
||||||
buildInputMediaDocument,
|
buildInputMediaDocument,
|
||||||
buildInputPeer,
|
buildInputPeer,
|
||||||
|
buildInputPhoto,
|
||||||
buildInputPoll,
|
buildInputPoll,
|
||||||
buildInputPollFromExisting,
|
buildInputPollFromExisting,
|
||||||
buildInputReaction,
|
buildInputReaction,
|
||||||
buildInputReplyTo,
|
buildInputReplyTo,
|
||||||
buildInputStory,
|
buildInputStory,
|
||||||
|
buildInputSuggestedPost,
|
||||||
buildInputTextWithEntities,
|
buildInputTextWithEntities,
|
||||||
buildInputTodo,
|
buildInputTodo,
|
||||||
buildInputUser,
|
buildInputUser,
|
||||||
@ -98,6 +102,7 @@ import {
|
|||||||
deserializeBytes,
|
deserializeBytes,
|
||||||
resolveMessageApiChatId,
|
resolveMessageApiChatId,
|
||||||
} from '../helpers/misc';
|
} from '../helpers/misc';
|
||||||
|
import localDb from '../localDb';
|
||||||
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
|
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
|
||||||
import { processMessageAndUpdateThreadInfo } from '../updates/entityProcessor';
|
import { processMessageAndUpdateThreadInfo } from '../updates/entityProcessor';
|
||||||
import { processAffectedHistory, updateChannelState } from '../updates/updateManager';
|
import { processAffectedHistory, updateChannelState } from '../updates/updateManager';
|
||||||
@ -266,8 +271,8 @@ export function sendMessageLocal(
|
|||||||
params: SendMessageParams,
|
params: SendMessageParams,
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
chat, lastMessageId, text, entities, replyInfo, attachment, sticker, story, gif, poll, todo, contact,
|
chat, lastMessageId, text, entities, replyInfo, suggestedPostInfo, attachment, sticker, story, gif, poll, todo,
|
||||||
scheduledAt, groupedId, sendAs, wasDrafted, isInvertedMedia, effectId, isPending, messagePriceInStars,
|
contact, scheduledAt, groupedId, sendAs, wasDrafted, isInvertedMedia, effectId, isPending, messagePriceInStars,
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
if (!chat) return undefined;
|
if (!chat) return undefined;
|
||||||
@ -281,6 +286,7 @@ export function sendMessageLocal(
|
|||||||
text,
|
text,
|
||||||
entities,
|
entities,
|
||||||
replyInfo,
|
replyInfo,
|
||||||
|
suggestedPostInfo,
|
||||||
attachment,
|
attachment,
|
||||||
sticker,
|
sticker,
|
||||||
gif,
|
gif,
|
||||||
@ -315,7 +321,9 @@ export function sendApiMessage(
|
|||||||
onProgress?: ApiOnProgress,
|
onProgress?: ApiOnProgress,
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
chat, text, entities, replyInfo, attachment, sticker, story, gif, poll, todo, contact,
|
chat, text, entities, replyInfo, suggestedPostInfo, suggestedMedia,
|
||||||
|
attachment, sticker, story, gif, poll, todo, contact,
|
||||||
|
|
||||||
isSilent, scheduledAt, groupedId, noWebPage, sendAs, shouldUpdateStickerSetOrder,
|
isSilent, scheduledAt, groupedId, noWebPage, sendAs, shouldUpdateStickerSetOrder,
|
||||||
isInvertedMedia, effectId, webPageMediaSize, webPageUrl, messagePriceInStars,
|
isInvertedMedia, effectId, webPageMediaSize, webPageUrl, messagePriceInStars,
|
||||||
} = params;
|
} = params;
|
||||||
@ -343,6 +351,7 @@ export function sendApiMessage(
|
|||||||
text,
|
text,
|
||||||
entities,
|
entities,
|
||||||
replyInfo,
|
replyInfo,
|
||||||
|
suggestedPostInfo,
|
||||||
attachment: attachment!,
|
attachment: attachment!,
|
||||||
groupedId,
|
groupedId,
|
||||||
isSilent,
|
isSilent,
|
||||||
@ -353,7 +362,43 @@ export function sendApiMessage(
|
|||||||
|
|
||||||
const messagePromise = (async () => {
|
const messagePromise = (async () => {
|
||||||
let media: GramJs.TypeInputMedia | undefined;
|
let media: GramJs.TypeInputMedia | undefined;
|
||||||
if (attachment) {
|
|
||||||
|
if (suggestedPostInfo && suggestedMedia && !attachment) {
|
||||||
|
if (suggestedMedia.photo) {
|
||||||
|
const inputPhoto = buildInputPhoto(suggestedMedia.photo);
|
||||||
|
if (inputPhoto) {
|
||||||
|
media = new GramJs.InputMediaPhoto({
|
||||||
|
id: inputPhoto,
|
||||||
|
spoiler: suggestedMedia.photo.isSpoiler || undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (suggestedMedia.video) {
|
||||||
|
const inputDocument = buildInputDocument(suggestedMedia.video);
|
||||||
|
if (inputDocument) {
|
||||||
|
media = new GramJs.InputMediaDocument({
|
||||||
|
id: inputDocument,
|
||||||
|
spoiler: suggestedMedia.video.isSpoiler || undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (suggestedMedia.document) {
|
||||||
|
const document = suggestedMedia.document;
|
||||||
|
if (document.id) {
|
||||||
|
const localDocument = localDb.documents[document.id];
|
||||||
|
if (localDocument) {
|
||||||
|
const inputDocument = new GramJs.InputDocument({
|
||||||
|
id: localDocument.id,
|
||||||
|
accessHash: localDocument.accessHash,
|
||||||
|
fileReference: localDocument.fileReference,
|
||||||
|
});
|
||||||
|
media = new GramJs.InputMediaDocument({
|
||||||
|
id: inputDocument,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!media && attachment) {
|
||||||
try {
|
try {
|
||||||
media = await uploadMedia(localMessage, attachment, onProgress!);
|
media = await uploadMedia(localMessage, attachment, onProgress!);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -416,6 +461,7 @@ export function sendApiMessage(
|
|||||||
invertMedia: isInvertedMedia || undefined,
|
invertMedia: isInvertedMedia || undefined,
|
||||||
effect: effectId ? BigInt(effectId) : undefined,
|
effect: effectId ? BigInt(effectId) : undefined,
|
||||||
allowPaidStars: messagePriceInStars ? BigInt(messagePriceInStars) : undefined,
|
allowPaidStars: messagePriceInStars ? BigInt(messagePriceInStars) : undefined,
|
||||||
|
suggestedPost: suggestedPostInfo && buildInputSuggestedPost(suggestedPostInfo),
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -477,6 +523,7 @@ function sendGroupedMedia(
|
|||||||
text = DEFAULT_PRIMITIVES.STRING,
|
text = DEFAULT_PRIMITIVES.STRING,
|
||||||
entities,
|
entities,
|
||||||
replyInfo,
|
replyInfo,
|
||||||
|
suggestedPostInfo,
|
||||||
attachment,
|
attachment,
|
||||||
groupedId,
|
groupedId,
|
||||||
isSilent,
|
isSilent,
|
||||||
@ -488,6 +535,7 @@ function sendGroupedMedia(
|
|||||||
text?: string;
|
text?: string;
|
||||||
entities?: ApiMessageEntity[];
|
entities?: ApiMessageEntity[];
|
||||||
replyInfo?: ApiInputReplyInfo;
|
replyInfo?: ApiInputReplyInfo;
|
||||||
|
suggestedPostInfo?: ApiInputSuggestedPostInfo;
|
||||||
attachment: ApiAttachment;
|
attachment: ApiAttachment;
|
||||||
groupedId: string;
|
groupedId: string;
|
||||||
isSilent?: boolean;
|
isSilent?: boolean;
|
||||||
@ -571,6 +619,7 @@ function sendGroupedMedia(
|
|||||||
...(scheduledAt && { scheduleDate: scheduledAt }),
|
...(scheduledAt && { scheduleDate: scheduledAt }),
|
||||||
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
||||||
...(messagePriceInStars && { allowPaidStars: BigInt(messagePriceInStars * count) }),
|
...(messagePriceInStars && { allowPaidStars: BigInt(messagePriceInStars * count) }),
|
||||||
|
...(suggestedPostInfo && { suggestedPost: buildInputSuggestedPost(suggestedPostInfo) }),
|
||||||
}), {
|
}), {
|
||||||
shouldIgnoreUpdates: true,
|
shouldIgnoreUpdates: true,
|
||||||
});
|
});
|
||||||
@ -1075,6 +1124,30 @@ export async function deleteSavedHistory({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function toggleSuggestedPostApproval({
|
||||||
|
chat,
|
||||||
|
messageId,
|
||||||
|
reject,
|
||||||
|
scheduleDate,
|
||||||
|
rejectComment,
|
||||||
|
}: {
|
||||||
|
chat: ApiChat;
|
||||||
|
messageId: number;
|
||||||
|
reject?: boolean;
|
||||||
|
scheduleDate?: number;
|
||||||
|
rejectComment?: string;
|
||||||
|
}) {
|
||||||
|
const result = await invokeRequest(new GramJs.messages.ToggleSuggestedPostApproval({
|
||||||
|
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||||
|
msgId: messageId,
|
||||||
|
reject: reject || undefined,
|
||||||
|
scheduleDate,
|
||||||
|
rejectComment,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export async function reportMessages({
|
export async function reportMessages({
|
||||||
peer, messageIds, description, option,
|
peer, messageIds, description, option,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import type {
|
|||||||
ApiStarGiftRegular,
|
ApiStarGiftRegular,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
|
import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
||||||
import { buildApiResaleGifts, buildApiSavedStarGift, buildApiStarGift,
|
import { buildApiResaleGifts, buildApiSavedStarGift, buildApiStarGift,
|
||||||
buildApiStarGiftAttribute, buildInputResaleGiftsAttributes } from '../apiBuilders/gifts';
|
buildApiStarGiftAttribute, buildInputResaleGiftsAttributes } from '../apiBuilders/gifts';
|
||||||
import {
|
import {
|
||||||
@ -20,6 +21,7 @@ import {
|
|||||||
buildApiStarsTransaction,
|
buildApiStarsTransaction,
|
||||||
buildApiStarTopupOption,
|
buildApiStarTopupOption,
|
||||||
} from '../apiBuilders/payments';
|
} from '../apiBuilders/payments';
|
||||||
|
import { buildApiUser } from '../apiBuilders/users';
|
||||||
import { buildInputPeer, buildInputSavedStarGift, buildInputUser, DEFAULT_PRIMITIVES } from '../gramjsBuilders';
|
import { buildInputPeer, buildInputSavedStarGift, buildInputUser, DEFAULT_PRIMITIVES } from '../gramjsBuilders';
|
||||||
import { checkErrorType, wrapError } from '../helpers/misc';
|
import { checkErrorType, wrapError } from '../helpers/misc';
|
||||||
import { invokeRequest } from './client';
|
import { invokeRequest } from './client';
|
||||||
@ -44,8 +46,18 @@ export async function fetchStarGifts() {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const chats = result.chats?.map((chat) => buildApiChatFromPreview(chat)).filter(Boolean);
|
||||||
|
const users = result.users?.map(buildApiUser).filter(Boolean);
|
||||||
|
|
||||||
// Right now, only regular star gifts can be bought, but API are not specific
|
// Right now, only regular star gifts can be bought, but API are not specific
|
||||||
return result.gifts.map(buildApiStarGift).filter((gift): gift is ApiStarGiftRegular => gift.type === 'starGift');
|
const gifts
|
||||||
|
= result.gifts.map(buildApiStarGift).filter((gift): gift is ApiStarGiftRegular => gift.type === 'starGift');
|
||||||
|
|
||||||
|
return {
|
||||||
|
gifts,
|
||||||
|
chats,
|
||||||
|
users,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchResaleGifts({
|
export async function fetchResaleGifts({
|
||||||
@ -179,12 +191,18 @@ export async function fetchStarsStatus() {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const balance = buildApiStarsAmount(result.balance);
|
||||||
|
if (!balance) {
|
||||||
|
// For now, skip if balance is in TON
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nextHistoryOffset: result.nextOffset,
|
nextHistoryOffset: result.nextOffset,
|
||||||
history: result.history?.map(buildApiStarsTransaction),
|
history: result.history?.map(buildApiStarsTransaction).filter(Boolean),
|
||||||
nextSubscriptionOffset: result.subscriptionsNextOffset,
|
nextSubscriptionOffset: result.subscriptionsNextOffset,
|
||||||
subscriptions: result.subscriptions?.map(buildApiStarsSubscription),
|
subscriptions: result.subscriptions?.map(buildApiStarsSubscription),
|
||||||
balance: buildApiStarsAmount(result.balance),
|
balance,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,10 +232,16 @@ export async function fetchStarsTransactions({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const balance = buildApiStarsAmount(result.balance);
|
||||||
|
if (!balance) {
|
||||||
|
// For now, skip if balance is in TON
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nextOffset: result.nextOffset,
|
nextOffset: result.nextOffset,
|
||||||
history: result.history?.map(buildApiStarsTransaction),
|
history: result.history?.map(buildApiStarsTransaction).filter(Boolean),
|
||||||
balance: buildApiStarsAmount(result.balance),
|
balance,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,10 +285,16 @@ export async function fetchStarsSubscriptions({
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const balance = buildApiStarsAmount(result.balance);
|
||||||
|
if (!balance) {
|
||||||
|
// For now, skip if balance is in TON
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nextOffset: result.subscriptionsNextOffset,
|
nextOffset: result.subscriptionsNextOffset,
|
||||||
subscriptions: result.subscriptions.map(buildApiStarsSubscription),
|
subscriptions: result.subscriptions.map(buildApiStarsSubscription),
|
||||||
balance: buildApiStarsAmount(result.balance),
|
balance,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export async function fetchChannelMonetizationStatistics({
|
|||||||
peer: ApiPeer;
|
peer: ApiPeer;
|
||||||
dcId?: number;
|
dcId?: number;
|
||||||
}) {
|
}) {
|
||||||
const result = await invokeRequest(new GramJs.stats.GetBroadcastRevenueStats({
|
const result = await invokeRequest(new GramJs.payments.GetStarsRevenueStats({
|
||||||
peer: buildInputPeer(peer.id, peer.accessHash),
|
peer: buildInputPeer(peer.id, peer.accessHash),
|
||||||
}), {
|
}), {
|
||||||
dcId,
|
dcId,
|
||||||
@ -234,7 +234,7 @@ export async function fetchMonetizationRevenueWithdrawalUrl({
|
|||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await invokeRequest(new GramJs.stats.GetBroadcastRevenueWithdrawalUrl({
|
const result = await invokeRequest(new GramJs.payments.GetStarsRevenueWithdrawalUrl({
|
||||||
peer: buildInputPeer(peer.id, peer.accessHash),
|
peer: buildInputPeer(peer.id, peer.accessHash),
|
||||||
password,
|
password,
|
||||||
}), {
|
}), {
|
||||||
|
|||||||
@ -1042,9 +1042,14 @@ export function updater(update: Update) {
|
|||||||
isEnabled: update.enabled ? true : undefined,
|
isEnabled: update.enabled ? true : undefined,
|
||||||
});
|
});
|
||||||
} else if (update instanceof GramJs.UpdateStarsBalance) {
|
} else if (update instanceof GramJs.UpdateStarsBalance) {
|
||||||
|
const balance = buildApiStarsAmount(update.balance);
|
||||||
|
if (!balance) {
|
||||||
|
// Skip TON balance updates for now
|
||||||
|
return;
|
||||||
|
}
|
||||||
sendApiUpdate({
|
sendApiUpdate({
|
||||||
'@type': 'updateStarsBalance',
|
'@type': 'updateStarsBalance',
|
||||||
balance: buildApiStarsAmount(update.balance),
|
balance,
|
||||||
});
|
});
|
||||||
} else if (update instanceof GramJs.UpdatePaidReactionPrivacy) {
|
} else if (update instanceof GramJs.UpdatePaidReactionPrivacy) {
|
||||||
sendApiUpdate({
|
sendApiUpdate({
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { ApiBotCommand } from './bots';
|
import type { ApiBotCommand } from './bots';
|
||||||
import type {
|
import type {
|
||||||
ApiChatReactions, ApiFormattedText, ApiInputMessageReplyInfo, ApiPhoto, ApiStickerSet,
|
ApiChatReactions, ApiFormattedText, ApiInputMessageReplyInfo, ApiInputSuggestedPostInfo, ApiPhoto, ApiStickerSet,
|
||||||
} from './messages';
|
} from './messages';
|
||||||
import type { ApiBotVerification, ApiChatInviteImporter, ApiPeerNotifySettings } from './misc';
|
import type { ApiBotVerification, ApiChatInviteImporter, ApiPeerNotifySettings } from './misc';
|
||||||
import type {
|
import type {
|
||||||
@ -318,6 +318,7 @@ export interface ApiChatLink {
|
|||||||
export type ApiDraft = {
|
export type ApiDraft = {
|
||||||
text?: ApiFormattedText;
|
text?: ApiFormattedText;
|
||||||
replyInfo?: ApiInputMessageReplyInfo;
|
replyInfo?: ApiInputMessageReplyInfo;
|
||||||
|
suggestedPostInfo?: ApiInputSuggestedPostInfo;
|
||||||
date?: number;
|
date?: number;
|
||||||
effectId?: string;
|
effectId?: string;
|
||||||
isLocal?: boolean;
|
isLocal?: boolean;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { ApiGroupCall, ApiPhoneCallDiscardReason } from './calls';
|
import type { ApiGroupCall, ApiPhoneCallDiscardReason } from './calls';
|
||||||
import type { ApiBotApp, ApiFormattedText, ApiPhoto } from './messages';
|
import type { ApiBotApp, ApiFormattedText, ApiPhoto } from './messages';
|
||||||
import type { ApiTodoItem } from './messages';
|
import type { ApiTodoItem } from './messages';
|
||||||
import type { ApiStarGiftRegular, ApiStarGiftUnique } from './stars';
|
import type { ApiStarGiftRegular, ApiStarGiftUnique, ApiStarsAmount } from './stars';
|
||||||
|
|
||||||
interface ActionMediaType {
|
interface ActionMediaType {
|
||||||
mediaType: 'action';
|
mediaType: 'action';
|
||||||
@ -282,6 +282,25 @@ export interface ApiMessageActionPaidMessagesPrice extends ActionMediaType {
|
|||||||
isAllowedInChannel?: boolean;
|
isAllowedInChannel?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiMessageActionSuggestedPostApproval extends ActionMediaType {
|
||||||
|
type: 'suggestedPostApproval';
|
||||||
|
isRejected?: boolean;
|
||||||
|
isBalanceTooLow?: boolean;
|
||||||
|
rejectComment?: string;
|
||||||
|
scheduleDate?: number;
|
||||||
|
amount?: ApiStarsAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiMessageActionSuggestedPostSuccess extends ActionMediaType {
|
||||||
|
type: 'suggestedPostSuccess';
|
||||||
|
amount?: ApiStarsAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiMessageActionSuggestedPostRefund extends ActionMediaType {
|
||||||
|
type: 'suggestedPostRefund';
|
||||||
|
payerInitiated: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ApiMessageActionTodoCompletions extends ActionMediaType {
|
export interface ApiMessageActionTodoCompletions extends ActionMediaType {
|
||||||
type: 'todoCompletions';
|
type: 'todoCompletions';
|
||||||
completedIds: number[];
|
completedIds: number[];
|
||||||
@ -310,5 +329,6 @@ export type ApiMessageAction = ApiMessageActionUnsupported | ApiMessageActionCha
|
|||||||
| ApiMessageActionChannelJoined | ApiMessageActionGiftCode | ApiMessageActionGiveawayLaunch
|
| ApiMessageActionChannelJoined | ApiMessageActionGiftCode | ApiMessageActionGiveawayLaunch
|
||||||
| ApiMessageActionGiveawayResults | ApiMessageActionPaymentRefunded | ApiMessageActionGiftStars
|
| ApiMessageActionGiveawayResults | ApiMessageActionPaymentRefunded | ApiMessageActionGiftStars
|
||||||
| ApiMessageActionPrizeStars | ApiMessageActionStarGift | ApiMessageActionStarGiftUnique
|
| ApiMessageActionPrizeStars | ApiMessageActionStarGift | ApiMessageActionStarGiftUnique
|
||||||
| ApiMessageActionPaidMessagesRefunded | ApiMessageActionPaidMessagesPrice | ApiMessageActionTodoCompletions
|
| ApiMessageActionPaidMessagesRefunded | ApiMessageActionPaidMessagesPrice | ApiMessageActionSuggestedPostApproval
|
||||||
|
| ApiMessageActionSuggestedPostSuccess | ApiMessageActionSuggestedPostRefund | ApiMessageActionTodoCompletions
|
||||||
| ApiMessageActionTodoAppendTasks;
|
| ApiMessageActionTodoAppendTasks;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import type { ApiMessageAction } from './messageActions';
|
|||||||
import type {
|
import type {
|
||||||
ApiLabeledPrice,
|
ApiLabeledPrice,
|
||||||
} from './payments';
|
} from './payments';
|
||||||
import type { ApiStarGiftUnique } from './stars';
|
import type { ApiStarGiftUnique, ApiStarsAmount } from './stars';
|
||||||
import type {
|
import type {
|
||||||
ApiMessageStoryData, ApiStory, ApiWebPageStickerData, ApiWebPageStoryData,
|
ApiMessageStoryData, ApiStory, ApiWebPageStickerData, ApiWebPageStoryData,
|
||||||
} from './stories';
|
} from './stories';
|
||||||
@ -412,12 +412,26 @@ export interface ApiInputMessageReplyInfo {
|
|||||||
quoteOffset?: number;
|
quoteOffset?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiSuggestedPost {
|
||||||
|
isAccepted?: true;
|
||||||
|
isRejected?: true;
|
||||||
|
price?: ApiStarsAmount;
|
||||||
|
scheduleDate?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ApiInputStoryReplyInfo {
|
export interface ApiInputStoryReplyInfo {
|
||||||
type: 'story';
|
type: 'story';
|
||||||
peerId: string;
|
peerId: string;
|
||||||
storyId: number;
|
storyId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiInputSuggestedPostInfo {
|
||||||
|
price?: ApiStarsAmount;
|
||||||
|
scheduleDate?: number;
|
||||||
|
isAccepted?: true;
|
||||||
|
isRejected?: true;
|
||||||
|
}
|
||||||
|
|
||||||
export type ApiInputReplyInfo = ApiInputMessageReplyInfo | ApiInputStoryReplyInfo;
|
export type ApiInputReplyInfo = ApiInputMessageReplyInfo | ApiInputStoryReplyInfo;
|
||||||
|
|
||||||
export interface ApiMessageForwardInfo {
|
export interface ApiMessageForwardInfo {
|
||||||
@ -577,6 +591,7 @@ export interface ApiMessage {
|
|||||||
isOutgoing: boolean;
|
isOutgoing: boolean;
|
||||||
senderId?: string;
|
senderId?: string;
|
||||||
replyInfo?: ApiReplyInfo;
|
replyInfo?: ApiReplyInfo;
|
||||||
|
suggestedPostInfo?: ApiInputSuggestedPostInfo;
|
||||||
sendingState?: 'messageSendingStatePending' | 'messageSendingStateFailed';
|
sendingState?: 'messageSendingStatePending' | 'messageSendingStateFailed';
|
||||||
forwardInfo?: ApiMessageForwardInfo;
|
forwardInfo?: ApiMessageForwardInfo;
|
||||||
isDeleting?: boolean;
|
isDeleting?: boolean;
|
||||||
@ -866,6 +881,13 @@ interface ApiKeyboardButtonCopy {
|
|||||||
copyText: string;
|
copyText: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiKeyboardButtonSuggestedMessage {
|
||||||
|
type: 'suggestedMessage';
|
||||||
|
text: string;
|
||||||
|
buttonType: 'approve' | 'decline' | 'suggestChanges';
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export type ApiKeyboardButton = (
|
export type ApiKeyboardButton = (
|
||||||
ApiKeyboardButtonSimple
|
ApiKeyboardButtonSimple
|
||||||
| ApiKeyboardButtonReceipt
|
| ApiKeyboardButtonReceipt
|
||||||
@ -878,6 +900,7 @@ export type ApiKeyboardButton = (
|
|||||||
| ApiKeyboardButtonSimpleWebView
|
| ApiKeyboardButtonSimpleWebView
|
||||||
| ApiKeyboardButtonUrlAuth
|
| ApiKeyboardButtonUrlAuth
|
||||||
| ApiKeyboardButtonCopy
|
| ApiKeyboardButtonCopy
|
||||||
|
| ApiKeyboardButtonSuggestedMessage
|
||||||
);
|
);
|
||||||
|
|
||||||
export type ApiKeyboardButtons = ApiKeyboardButton[][];
|
export type ApiKeyboardButtons = ApiKeyboardButton[][];
|
||||||
|
|||||||
@ -252,6 +252,13 @@ export interface ApiAppConfig {
|
|||||||
starsStargiftResaleAmountMin?: number;
|
starsStargiftResaleAmountMin?: number;
|
||||||
starsStargiftResaleAmountMax?: number;
|
starsStargiftResaleAmountMax?: number;
|
||||||
starsStargiftResaleCommissionPermille?: number;
|
starsStargiftResaleCommissionPermille?: number;
|
||||||
|
starsSuggestedPostAmountMax?: number;
|
||||||
|
starsSuggestedPostAmountMin?: number;
|
||||||
|
starsSuggestedPostCommissionPermille?: number;
|
||||||
|
starsSuggestedPostAgeMin?: number;
|
||||||
|
starsSuggestedPostFutureMax?: number;
|
||||||
|
starsSuggestedPostFutureMin?: number;
|
||||||
|
tonSuggestedPostCommissionPermille?: number;
|
||||||
pollMaxAnswers?: number;
|
pollMaxAnswers?: number;
|
||||||
todoItemsMax?: number;
|
todoItemsMax?: number;
|
||||||
todoTitleLengthMax?: number;
|
todoTitleLengthMax?: number;
|
||||||
|
|||||||
@ -19,6 +19,7 @@ export interface ApiStarGiftRegular {
|
|||||||
isBirthday?: true;
|
isBirthday?: true;
|
||||||
upgradeStars?: number;
|
upgradeStars?: number;
|
||||||
resellMinStars?: number;
|
resellMinStars?: number;
|
||||||
|
releasedByPeerId?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +37,7 @@ export interface ApiStarGiftUnique {
|
|||||||
slug: string;
|
slug: string;
|
||||||
giftAddress?: string;
|
giftAddress?: string;
|
||||||
resellPriceInStars?: number;
|
resellPriceInStars?: number;
|
||||||
|
releasedByPeerId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiStarGift = ApiStarGiftRegular | ApiStarGiftUnique;
|
export type ApiStarGift = ApiStarGiftRegular | ApiStarGiftUnique;
|
||||||
|
|||||||
@ -2025,6 +2025,70 @@
|
|||||||
"MonoforumComposerPlaceholder" = "Choose a message to reply";
|
"MonoforumComposerPlaceholder" = "Choose a message to reply";
|
||||||
"ChannelSendMessage" = "Direct Messages";
|
"ChannelSendMessage" = "Direct Messages";
|
||||||
"AutomaticTranslation" = "Automatic Translation";
|
"AutomaticTranslation" = "Automatic Translation";
|
||||||
|
"ComposerEmbeddedMessageSuggestedPostTitle" = "Suggest a Post Below";
|
||||||
|
"ComposerEmbeddedMessageSuggestedPostDescription" = "Tap to offer a price for publishing";
|
||||||
|
"TitleSuggestedPostAmountForAnyTime" = "{amount} for publishing anytime";
|
||||||
|
"ActionSuggestedPostOutgoing" = "**You** suggest to post this message.";
|
||||||
|
"ActionSuggestedPostIncoming" = "**{user}** suggest to post this message.";
|
||||||
|
"ActionSuggestedChangesPrice" = "price";
|
||||||
|
"ActionSuggestedChangesText" = "text";
|
||||||
|
"ActionSuggestedChangesTime" = "time";
|
||||||
|
"ActionSuggestedChangesMedia" = "media";
|
||||||
|
"ActionSuggestedChangesOutgoing" = "**You** suggested a new {changes} for this post.";
|
||||||
|
"ActionSuggestedChangesIncoming" = "**{user}** suggested a new {changes} for this post.";
|
||||||
|
"TitlePrice" = "Price";
|
||||||
|
"TitleTime" = "Time";
|
||||||
|
"TitleSuggestMessage" = "Suggest a Message";
|
||||||
|
"TitleSuggestedChanges" = "Suggest Changes";
|
||||||
|
"EnterPriceInStars" = "Enter Price In Stars";
|
||||||
|
"SuggestMessagePriceDescription" = "Choose how many {currency} to pay to publish this post.";
|
||||||
|
"SuggestMessageDateTimeHint" = "Select the date and time you want the post to be published.";
|
||||||
|
"SuggestMessageTimeDescription" = "{hint} The post will remain available for at least {duration} from this date.";
|
||||||
|
"TitleAnytime" = "Anytime";
|
||||||
|
"ButtonOfferAmount" = "Offer {amount}";
|
||||||
|
"ButtonOfferFree" = "Offer Free";
|
||||||
|
"ButtonUpdateTerms" = "Update Terms";
|
||||||
|
"InputPlaceholderPrice" = "Enter Price";
|
||||||
|
"SuggestedPostApprove" = "Approve";
|
||||||
|
"SuggestedPostDecline" = "Decline";
|
||||||
|
"SuggestedPostSuggestChanges" = "Suggest Changes";
|
||||||
|
"InputTitleSuggestMessageTime" = "Publishing Time";
|
||||||
|
"SuggestedPostApproved" = "Suggested post approved";
|
||||||
|
"SuggestedPostRejectedNotification" = "Suggested post rejected";
|
||||||
|
"SuggestedPostAgreementReached" = "🤝 Agreement reached!";
|
||||||
|
"SuggestedPostPublishSchedule" = "📅 This post will be automatically published on {peer} **{date}**.";
|
||||||
|
"SuggestedPostPublishScheduleYou" = "📅 Your post will be automatically published on {peer} **{date}**.";
|
||||||
|
"SuggestedPostPublished" = "📅 This post was automatically published on {peer} **{date}**.";
|
||||||
|
"SuggestedPostPublishedYou" = "📅 Your post was automatically published on {peer} **{date}**.";
|
||||||
|
"SuggestedPostCharged" = "💰 {user} has been charged {amount}.";
|
||||||
|
"SuggestedPostChargedYou" = "💰 You have been charged {amount}.";
|
||||||
|
"SuggestedPostReceiveAmount" = "⏳ {peer} will receive the {currency} once the post has been live for {duration}.";
|
||||||
|
"SuggestedPostReceiveAmountYou" = "⏳ {peer} will receive your {currency} once the post has been live for {duration}.";
|
||||||
|
"SuggestedPostRefund" = "🔄 If {peer} removes the post before it has been live for {duration}, payment will be refunded.";
|
||||||
|
"SuggestedPostRefundYou" = "🔄 If {peer} removes the post before it has been live for {duration}, your {currency} will be refunded.";
|
||||||
|
"SuggestedPostBalanceTooLow" = "⚠️ **Transaction failed** because {peer} didn't have enough {currency}.";
|
||||||
|
"SuggestedPostRefundedByUser" = "{channel} will not receive {amount} because {user} requested a refund.";
|
||||||
|
"SuggestedPostRefundedByChannel" = "{amount} was returned to {peer} because {channel} deleted the message.";
|
||||||
|
"CurrencyStars" = "Stars";
|
||||||
|
"DeclineReasonPlaceholder" = "Add a reason (optional)";
|
||||||
|
"DeclinePostDialogQuestion" = "Do you want to decline this post from **{sender}**?";
|
||||||
|
"SuggestedPostRejected" = "**{peer}** rejected this message.";
|
||||||
|
"SuggestedPostRejectedYou" = "You rejected this message.";
|
||||||
|
"SuggestedPostRejectedWithReason" = "**{peer}** rejected this message with the comment.";
|
||||||
|
"SuggestedPostRejectedWithReasonYou" = "You rejected this message with the comment.";
|
||||||
|
"SuggestedPostRejectedComment" = "\"{comment}\"";
|
||||||
|
"ActionSuggestedPostSuccess" = "{channel} has received {amount} for publishing post";
|
||||||
|
"ComposerPlaceholderCaption" = "Caption";
|
||||||
|
"DescriptionSuggestedPostMinimumOffer" = "Minimum offer is **{amount}**.";
|
||||||
|
"SuggestedPostConfirmTitle" = "Accept Terms";
|
||||||
|
"SuggestedPostConfirmMessage" = "Do you want to publish this post from **{peer}**?";
|
||||||
|
"SuggestedPostConfirmDetailsAdmin" = "You will receive **{amount}** ({commission}%) for publishing this post. It must remain visible for at least {duration} after publication.";
|
||||||
|
"SuggestedPostConfirmDetailsUser" = "You will pay **{amount}** ({commission}%) for publishing this post. It must remain visible for at least {duration} after publication.";
|
||||||
|
"SuggestedPostConfirmDetailsWithTimeAdmin" = "You will receive **{amount}** ({commission}%) for publishing this post **{time}**. It must remain visible for at least {duration} after publication.";
|
||||||
|
"SuggestedPostConfirmDetailsWithTimeUser" = "You will pay **{amount}** ({commission}%) for publishing this post **{time}**. It must remain visible for at least {duration} after publication.";
|
||||||
|
"ButtonPublish" = "Publish";
|
||||||
|
"ButtonPublishAtTime" = "Publish {time}";
|
||||||
|
"PublishNow" = "Publish Now";
|
||||||
"TitleNewToDoList" = "New Checklist";
|
"TitleNewToDoList" = "New Checklist";
|
||||||
"TitleEditToDoList" = "Edit Checklist";
|
"TitleEditToDoList" = "Edit Checklist";
|
||||||
"TitleAppendToDoList" = "Append Checklist";
|
"TitleAppendToDoList" = "Append Checklist";
|
||||||
@ -2061,4 +2125,5 @@
|
|||||||
"HintTodoListTasksCount" = "You can add {count} more tasks";
|
"HintTodoListTasksCount" = "You can add {count} more tasks";
|
||||||
"ToDoListErrorChooseTitle" = "Please enter a title.";
|
"ToDoListErrorChooseTitle" = "Please enter a title.";
|
||||||
"ToDoListErrorChooseTasks" = "Please enter at least one task.";
|
"ToDoListErrorChooseTasks" = "Please enter at least one task.";
|
||||||
|
"GiftInfoCollectibleBy" = "Collectible #{number} by **{owner}**";
|
||||||
"PremiumPreviewTodo" = "Checklists";
|
"PremiumPreviewTodo" = "Checklists";
|
||||||
@ -37,6 +37,9 @@ export { default as ReportModal } from '../components/modals/reportModal/ReportM
|
|||||||
export { default as PreparedMessageModal } from '../components/modals/preparedMessage/PreparedMessageModal';
|
export { default as PreparedMessageModal } from '../components/modals/preparedMessage/PreparedMessageModal';
|
||||||
export { default as SharePreparedMessageModal }
|
export { default as SharePreparedMessageModal }
|
||||||
from '../components/modals/sharePreparedMessage/SharePreparedMessageModal';
|
from '../components/modals/sharePreparedMessage/SharePreparedMessageModal';
|
||||||
|
export { default as SuggestMessageModal } from '../components/modals/suggestMessage/SuggestMessageModal';
|
||||||
|
export { default as SuggestedPostApprovalModal }
|
||||||
|
from '../components/modals/suggestedPostApproval/SuggestedPostApprovalModal';
|
||||||
export { default as CalendarModal } from '../components/common/CalendarModal';
|
export { default as CalendarModal } from '../components/common/CalendarModal';
|
||||||
export { default as DeleteMessageModal } from '../components/common/DeleteMessageModal';
|
export { default as DeleteMessageModal } from '../components/common/DeleteMessageModal';
|
||||||
export { default as PinMessageModal } from '../components/common/PinMessageModal';
|
export { default as PinMessageModal } from '../components/common/PinMessageModal';
|
||||||
|
|||||||
@ -30,6 +30,15 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.description {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
margin-left: 1rem;
|
||||||
|
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
.Button {
|
.Button {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
|
|
||||||
|
|||||||
@ -33,8 +33,10 @@ export type OwnProps = {
|
|||||||
withTimePicker?: boolean;
|
withTimePicker?: boolean;
|
||||||
submitButtonLabel?: string;
|
submitButtonLabel?: string;
|
||||||
secondButtonLabel?: string;
|
secondButtonLabel?: string;
|
||||||
|
description?: string;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSubmit: (date: Date) => void;
|
onSubmit: (date: Date) => void;
|
||||||
|
onDateChange?: (date: Date) => void;
|
||||||
onSecondButtonClick?: NoneToVoidFunction;
|
onSecondButtonClick?: NoneToVoidFunction;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -58,8 +60,10 @@ const CalendarModal: FC<OwnProps> = ({
|
|||||||
withTimePicker,
|
withTimePicker,
|
||||||
submitButtonLabel,
|
submitButtonLabel,
|
||||||
secondButtonLabel,
|
secondButtonLabel,
|
||||||
|
description,
|
||||||
onClose,
|
onClose,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
onDateChange,
|
||||||
onSecondButtonClick,
|
onSecondButtonClick,
|
||||||
}) => {
|
}) => {
|
||||||
const lang = useOldLang();
|
const lang = useOldLang();
|
||||||
@ -169,6 +173,7 @@ const CalendarModal: FC<OwnProps> = ({
|
|||||||
dateCopy.setMonth(currentMonth);
|
dateCopy.setMonth(currentMonth);
|
||||||
dateCopy.setFullYear(currentYear);
|
dateCopy.setFullYear(currentYear);
|
||||||
|
|
||||||
|
onDateChange?.(dateCopy);
|
||||||
return dateCopy;
|
return dateCopy;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -196,11 +201,12 @@ const CalendarModal: FC<OwnProps> = ({
|
|||||||
const date = new Date(selectedDate.getTime());
|
const date = new Date(selectedDate.getTime());
|
||||||
date.setHours(hours);
|
date.setHours(hours);
|
||||||
setSelectedDate(date);
|
setSelectedDate(date);
|
||||||
|
onDateChange?.(date);
|
||||||
|
|
||||||
const hoursStr = formatInputTime(hours);
|
const hoursStr = formatInputTime(hours);
|
||||||
setSelectedHours(hoursStr);
|
setSelectedHours(hoursStr);
|
||||||
e.target.value = hoursStr;
|
e.target.value = hoursStr;
|
||||||
}, [selectedDate]);
|
}, [selectedDate, onDateChange]);
|
||||||
|
|
||||||
const handleChangeMinutes = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChangeMinutes = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const value = e.target.value.replace(/[^\d]+/g, '');
|
const value = e.target.value.replace(/[^\d]+/g, '');
|
||||||
@ -215,11 +221,12 @@ const CalendarModal: FC<OwnProps> = ({
|
|||||||
const date = new Date(selectedDate.getTime());
|
const date = new Date(selectedDate.getTime());
|
||||||
date.setMinutes(minutes);
|
date.setMinutes(minutes);
|
||||||
setSelectedDate(date);
|
setSelectedDate(date);
|
||||||
|
onDateChange?.(date);
|
||||||
|
|
||||||
const minutesStr = formatInputTime(minutes);
|
const minutesStr = formatInputTime(minutes);
|
||||||
setSelectedMinutes(minutesStr);
|
setSelectedMinutes(minutesStr);
|
||||||
e.target.value = minutesStr;
|
e.target.value = minutesStr;
|
||||||
}, [selectedDate]);
|
}, [selectedDate, onDateChange]);
|
||||||
|
|
||||||
function renderTimePicker() {
|
function renderTimePicker() {
|
||||||
return (
|
return (
|
||||||
@ -331,6 +338,11 @@ const CalendarModal: FC<OwnProps> = ({
|
|||||||
{withTimePicker && renderTimePicker()}
|
{withTimePicker && renderTimePicker()}
|
||||||
|
|
||||||
<div className="footer">
|
<div className="footer">
|
||||||
|
{description && (
|
||||||
|
<div className="description">
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="footer">
|
<div className="footer">
|
||||||
<Button
|
<Button
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
|
|||||||
@ -235,6 +235,8 @@ type StateProps =
|
|||||||
isReactionPickerOpen?: boolean;
|
isReactionPickerOpen?: boolean;
|
||||||
shouldDisplayGiftsButton?: boolean;
|
shouldDisplayGiftsButton?: boolean;
|
||||||
isForwarding?: boolean;
|
isForwarding?: boolean;
|
||||||
|
isReplying?: boolean;
|
||||||
|
hasSuggestedPost?: boolean;
|
||||||
forwardedMessagesCount?: number;
|
forwardedMessagesCount?: number;
|
||||||
pollModal: TabState['pollModal'];
|
pollModal: TabState['pollModal'];
|
||||||
todoListModal: TabState['todoListModal'];
|
todoListModal: TabState['todoListModal'];
|
||||||
@ -358,6 +360,8 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
isReactionPickerOpen,
|
isReactionPickerOpen,
|
||||||
shouldDisplayGiftsButton,
|
shouldDisplayGiftsButton,
|
||||||
isForwarding,
|
isForwarding,
|
||||||
|
isReplying,
|
||||||
|
hasSuggestedPost,
|
||||||
forwardedMessagesCount,
|
forwardedMessagesCount,
|
||||||
pollModal,
|
pollModal,
|
||||||
todoListModal,
|
todoListModal,
|
||||||
@ -460,6 +464,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
hideEffectInComposer,
|
hideEffectInComposer,
|
||||||
updateChatSilentPosting,
|
updateChatSilentPosting,
|
||||||
updateInsertingPeerIdMention,
|
updateInsertingPeerIdMention,
|
||||||
|
updateDraftSuggestedPostInfo,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const oldLang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
@ -816,7 +821,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
getHtml,
|
getHtml,
|
||||||
setHtml,
|
setHtml,
|
||||||
editedMessage: editingMessage,
|
editedMessage: editingMessage,
|
||||||
isDisabled: isInStoryViewer || Boolean(requestedDraft) || isMonoforum,
|
isDisabled: isInStoryViewer || Boolean(requestedDraft) || (!hasSuggestedPost && isMonoforum),
|
||||||
});
|
});
|
||||||
|
|
||||||
const resetComposer = useLastCallback((shouldPreserveInput = false) => {
|
const resetComposer = useLastCallback((shouldPreserveInput = false) => {
|
||||||
@ -872,6 +877,8 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
}, [disallowedGifts]);
|
}, [disallowedGifts]);
|
||||||
|
|
||||||
const shouldShowGiftButton = Boolean(!isChatWithSelf && shouldDisplayGiftsButton && !areAllGiftsDisallowed);
|
const shouldShowGiftButton = Boolean(!isChatWithSelf && shouldDisplayGiftsButton && !areAllGiftsDisallowed);
|
||||||
|
const shouldShowSuggestedPostButton = isMonoforum && !editingMessage
|
||||||
|
&& !isForwarding && !isReplying && !draft?.suggestedPostInfo;
|
||||||
|
|
||||||
const showCustomEmojiPremiumNotification = useLastCallback(() => {
|
const showCustomEmojiPremiumNotification = useLastCallback(() => {
|
||||||
const notificationNumber = customEmojiNotificationNumber.current;
|
const notificationNumber = customEmojiNotificationNumber.current;
|
||||||
@ -1567,6 +1574,11 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
const handleGiftClick = useLastCallback(() => {
|
const handleGiftClick = useLastCallback(() => {
|
||||||
openGiftModal({ forUserId: chatId });
|
openGiftModal({ forUserId: chatId });
|
||||||
});
|
});
|
||||||
|
const handleSuggestPostClick = useLastCallback(() => {
|
||||||
|
updateDraftSuggestedPostInfo({
|
||||||
|
price: { amount: 0, nanos: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const handleToggleSilentPosting = useLastCallback(() => {
|
const handleToggleSilentPosting = useLastCallback(() => {
|
||||||
const newValue = !isSilentPosting;
|
const newValue = !isSilentPosting;
|
||||||
@ -1638,6 +1650,10 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isReplying && hasSuggestedPost) {
|
||||||
|
return lang('ComposerPlaceholderCaption');
|
||||||
|
}
|
||||||
|
|
||||||
if (chat?.adminRights?.anonymous) {
|
if (chat?.adminRights?.anonymous) {
|
||||||
return lang('ComposerPlaceholderAnonymous');
|
return lang('ComposerPlaceholderAnonymous');
|
||||||
}
|
}
|
||||||
@ -1658,7 +1674,8 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
return lang('ComposerPlaceholderNoText');
|
return lang('ComposerPlaceholderNoText');
|
||||||
}, [
|
}, [
|
||||||
activeVoiceRecording, botKeyboardPlaceholder, chat, inputPlaceholder, isChannel, isComposerBlocked,
|
activeVoiceRecording, botKeyboardPlaceholder, chat, inputPlaceholder, isChannel, isComposerBlocked,
|
||||||
isInStoryViewer, isSilentPosting, lang, replyToTopic, threadId, windowWidth, paidMessagesStars,
|
isInStoryViewer, isSilentPosting, lang, replyToTopic, isReplying, threadId, windowWidth, paidMessagesStars,
|
||||||
|
hasSuggestedPost,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -2166,6 +2183,17 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
<Icon name="gift" />
|
<Icon name="gift" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
{shouldShowSuggestedPostButton && (
|
||||||
|
<Button
|
||||||
|
round
|
||||||
|
faded
|
||||||
|
className="composer-action-button"
|
||||||
|
color="translucent"
|
||||||
|
onClick={handleSuggestPostClick}
|
||||||
|
>
|
||||||
|
<Icon name="cash-circle" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
{Boolean(botKeyboardMessageId) && !activeVoiceRecording && !editingMessage && (
|
{Boolean(botKeyboardMessageId) && !activeVoiceRecording && !editingMessage && (
|
||||||
<ResponsiveHoverButton
|
<ResponsiveHoverButton
|
||||||
className={buildClassName('composer-action-button', isBotKeyboardOpen && 'activated')}
|
className={buildClassName('composer-action-button', isBotKeyboardOpen && 'activated')}
|
||||||
@ -2479,6 +2507,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
|
|
||||||
const maxMessageLength = global.config?.maxMessageLength || DEFAULT_MAX_MESSAGE_LENGTH;
|
const maxMessageLength = global.config?.maxMessageLength || DEFAULT_MAX_MESSAGE_LENGTH;
|
||||||
const isForwarding = chatId === tabState.forwardMessages.toChatId;
|
const isForwarding = chatId === tabState.forwardMessages.toChatId;
|
||||||
|
const isReplying = Boolean(draft?.replyInfo);
|
||||||
|
const hasSuggestedPost = Boolean(draft?.suggestedPostInfo);
|
||||||
const starsBalance = global.stars?.balance.amount || 0;
|
const starsBalance = global.stars?.balance.amount || 0;
|
||||||
const isStarsBalanceModalOpen = Boolean(tabState.starsBalanceModal);
|
const isStarsBalanceModalOpen = Boolean(tabState.starsBalanceModal);
|
||||||
const isAccountFrozen = selectIsCurrentUserFrozen(global);
|
const isAccountFrozen = selectIsCurrentUserFrozen(global);
|
||||||
@ -2507,6 +2537,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
botKeyboardMessageId,
|
botKeyboardMessageId,
|
||||||
botKeyboardPlaceholder: keyboardMessage?.keyboardPlaceholder,
|
botKeyboardPlaceholder: keyboardMessage?.keyboardPlaceholder,
|
||||||
isForwarding,
|
isForwarding,
|
||||||
|
isReplying,
|
||||||
|
hasSuggestedPost,
|
||||||
forwardedMessagesCount: isForwarding ? forwardMessageIds!.length : undefined,
|
forwardedMessagesCount: isForwarding ? forwardMessageIds!.length : undefined,
|
||||||
pollModal: tabState.pollModal,
|
pollModal: tabState.pollModal,
|
||||||
todoListModal: tabState.todoListModal,
|
todoListModal: tabState.todoListModal,
|
||||||
|
|||||||
@ -63,6 +63,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.suggested-price-star-icon {
|
||||||
|
margin-left: 0rem;
|
||||||
|
text-indent: 0rem;
|
||||||
|
}
|
||||||
|
|
||||||
&--background-icons {
|
&--background-icons {
|
||||||
margin: -0.1875rem -0.375rem -0.1875rem -0.1875rem;
|
margin: -0.1875rem -0.375rem -0.1875rem -0.1875rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { useMemo, useRef } from '../../../lib/teact/teact';
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiChat,
|
ApiChat,
|
||||||
|
ApiInputSuggestedPostInfo,
|
||||||
ApiMessage, ApiPeer, ApiReplyInfo, MediaContainer,
|
ApiMessage, ApiPeer, ApiReplyInfo, MediaContainer,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||||
@ -22,8 +23,10 @@ import {
|
|||||||
import { getMediaContentTypeDescription } from '../../../global/helpers/messageSummary';
|
import { getMediaContentTypeDescription } from '../../../global/helpers/messageSummary';
|
||||||
import { getPeerTitle } from '../../../global/helpers/peers';
|
import { getPeerTitle } from '../../../global/helpers/peers';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
import { formatScheduledDateTime } from '../../../util/dates/dateFormat';
|
||||||
import { isUserId } from '../../../util/entities/ids';
|
import { isUserId } from '../../../util/entities/ids';
|
||||||
import freezeWhenClosed from '../../../util/hoc/freezeWhenClosed';
|
import freezeWhenClosed from '../../../util/hoc/freezeWhenClosed';
|
||||||
|
import { formatStarsAsIcon } from '../../../util/localization/format';
|
||||||
import { getPictogramDimensions } from '../helpers/mediaDimensions';
|
import { getPictogramDimensions } from '../helpers/mediaDimensions';
|
||||||
import renderText from '../helpers/renderText';
|
import renderText from '../helpers/renderText';
|
||||||
import { renderTextWithEntities } from '../helpers/renderTextWithEntities';
|
import { renderTextWithEntities } from '../helpers/renderTextWithEntities';
|
||||||
@ -47,6 +50,7 @@ import './EmbeddedMessage.scss';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
replyInfo?: ApiReplyInfo;
|
replyInfo?: ApiReplyInfo;
|
||||||
|
suggestedPostInfo?: ApiInputSuggestedPostInfo;
|
||||||
message?: ApiMessage;
|
message?: ApiMessage;
|
||||||
sender?: ApiPeer;
|
sender?: ApiPeer;
|
||||||
senderChat?: ApiChat;
|
senderChat?: ApiChat;
|
||||||
@ -72,6 +76,7 @@ const EmbeddedMessage: FC<OwnProps> = ({
|
|||||||
className,
|
className,
|
||||||
message,
|
message,
|
||||||
replyInfo,
|
replyInfo,
|
||||||
|
suggestedPostInfo,
|
||||||
sender,
|
sender,
|
||||||
senderChat,
|
senderChat,
|
||||||
forwardSender,
|
forwardSender,
|
||||||
@ -139,6 +144,35 @@ const EmbeddedMessage: FC<OwnProps> = ({
|
|||||||
const { handleClick, handleMouseDown } = useFastClick(onClick);
|
const { handleClick, handleMouseDown } = useFastClick(onClick);
|
||||||
|
|
||||||
function renderTextContent() {
|
function renderTextContent() {
|
||||||
|
const isFree = !(suggestedPostInfo?.price?.amount);
|
||||||
|
if (suggestedPostInfo) {
|
||||||
|
if (isFree && !suggestedPostInfo.scheduleDate) {
|
||||||
|
return lang('ComposerEmbeddedMessageSuggestedPostDescription');
|
||||||
|
}
|
||||||
|
const priceText = suggestedPostInfo.price
|
||||||
|
? formatStarsAsIcon(lang, suggestedPostInfo.price.amount, {
|
||||||
|
className: 'suggested-price-star-icon',
|
||||||
|
})
|
||||||
|
: '';
|
||||||
|
const scheduleText = suggestedPostInfo.scheduleDate
|
||||||
|
? formatScheduledDateTime(suggestedPostInfo.scheduleDate, lang, oldLang)
|
||||||
|
: '';
|
||||||
|
if (priceText && !scheduleText) {
|
||||||
|
return lang('TitleSuggestedPostAmountForAnyTime',
|
||||||
|
{ amount: priceText },
|
||||||
|
{
|
||||||
|
withNodes: true,
|
||||||
|
withMarkdown: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{priceText}
|
||||||
|
{scheduleText ? ` • ${scheduleText}` : ''}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (replyInfo?.type === 'message' && replyInfo.quoteText) {
|
if (replyInfo?.type === 'message' && replyInfo.quoteText) {
|
||||||
return renderTextWithEntities({
|
return renderTextWithEntities({
|
||||||
text: replyInfo.quoteText.text,
|
text: replyInfo.quoteText.text,
|
||||||
@ -186,6 +220,14 @@ const EmbeddedMessage: FC<OwnProps> = ({
|
|||||||
return renderText(title);
|
return renderText(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (suggestedPostInfo && replyInfo) {
|
||||||
|
return lang('TitleSuggestedChanges');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suggestedPostInfo) {
|
||||||
|
return lang('ComposerEmbeddedMessageSuggestedPostTitle');
|
||||||
|
}
|
||||||
|
|
||||||
if (!senderTitle && !forwardSendersTitle) {
|
if (!senderTitle && !forwardSendersTitle) {
|
||||||
return NBSP;
|
return NBSP;
|
||||||
}
|
}
|
||||||
@ -252,6 +294,7 @@ const EmbeddedMessage: FC<OwnProps> = ({
|
|||||||
mediaThumbnail && 'with-thumb',
|
mediaThumbnail && 'with-thumb',
|
||||||
'no-selection',
|
'no-selection',
|
||||||
composerForwardSenders && 'is-input-forward',
|
composerForwardSenders && 'is-input-forward',
|
||||||
|
suggestedPostInfo && 'is-suggested-post',
|
||||||
)}
|
)}
|
||||||
dir={lang.isRtl ? 'rtl' : undefined}
|
dir={lang.isRtl ? 'rtl' : undefined}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
|||||||
@ -13,18 +13,21 @@ import { SCHEDULED_WHEN_ONLINE } from '../../config';
|
|||||||
import {
|
import {
|
||||||
getMessageHtmlId,
|
getMessageHtmlId,
|
||||||
getMessageOriginalId,
|
getMessageOriginalId,
|
||||||
|
getSuggestedChangesActionText,
|
||||||
|
getSuggestedChangesInfo,
|
||||||
isActionMessage,
|
isActionMessage,
|
||||||
isOwnMessage,
|
isOwnMessage,
|
||||||
isServiceNotificationMessage,
|
isServiceNotificationMessage,
|
||||||
} from '../../global/helpers';
|
} from '../../global/helpers';
|
||||||
import { getPeerTitle } from '../../global/helpers/peers';
|
import { getPeerTitle } from '../../global/helpers/peers';
|
||||||
import { selectSender } from '../../global/selectors';
|
import { selectChatMessage, selectSender } from '../../global/selectors';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { formatHumanDate } from '../../util/dates/dateFormat';
|
import { formatHumanDate, formatScheduledDateTime } from '../../util/dates/dateFormat';
|
||||||
import { compact } from '../../util/iteratees';
|
import { compact } from '../../util/iteratees';
|
||||||
import { formatStarsAsText } from '../../util/localization/format';
|
import { formatStarsAsText } from '../../util/localization/format';
|
||||||
import { isAlbum } from './helpers/groupMessages';
|
import { isAlbum } from './helpers/groupMessages';
|
||||||
import { preventMessageInputBlur } from './helpers/preventMessageInputBlur';
|
import { preventMessageInputBlur } from './helpers/preventMessageInputBlur';
|
||||||
|
import { renderPeerLink } from './message/helpers/messageActions';
|
||||||
|
|
||||||
import useDerivedSignal from '../../hooks/useDerivedSignal';
|
import useDerivedSignal from '../../hooks/useDerivedSignal';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
@ -33,12 +36,15 @@ import usePreviousDeprecated from '../../hooks/usePreviousDeprecated';
|
|||||||
import useMessageObservers from './hooks/useMessageObservers';
|
import useMessageObservers from './hooks/useMessageObservers';
|
||||||
import useScrollHooks from './hooks/useScrollHooks';
|
import useScrollHooks from './hooks/useScrollHooks';
|
||||||
|
|
||||||
|
import MiniTable, { type TableEntry } from '../common/MiniTable';
|
||||||
import ActionMessage from './message/ActionMessage';
|
import ActionMessage from './message/ActionMessage';
|
||||||
import Message from './message/Message';
|
import Message from './message/Message';
|
||||||
import SenderGroupContainer from './message/SenderGroupContainer';
|
import SenderGroupContainer from './message/SenderGroupContainer';
|
||||||
import SponsoredMessage from './message/SponsoredMessage';
|
import SponsoredMessage from './message/SponsoredMessage';
|
||||||
import MessageListAccountInfo from './MessageListAccountInfo';
|
import MessageListAccountInfo from './MessageListAccountInfo';
|
||||||
|
|
||||||
|
import actionMessageStyles from './message/ActionMessage.module.scss';
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
canShowAds?: boolean;
|
canShowAds?: boolean;
|
||||||
chatId: string;
|
chatId: string;
|
||||||
@ -176,6 +182,54 @@ const MessageListContent: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderSuggestedPostInfoAction = (message: ApiMessage) => {
|
||||||
|
if (message.suggestedPostInfo) {
|
||||||
|
const { price, scheduleDate } = message.suggestedPostInfo;
|
||||||
|
const sender = selectSender(getGlobal(), message);
|
||||||
|
const userTitle = sender ? getPeerTitle(lang, sender) : '';
|
||||||
|
const userLink = renderPeerLink(sender?.id, userTitle || lang('ActionFallbackUser'));
|
||||||
|
|
||||||
|
const originalMessage = message.replyInfo?.type === 'message' && message.replyInfo.replyToMsgId
|
||||||
|
? selectChatMessage(getGlobal(), message.chatId, message.replyInfo.replyToMsgId)
|
||||||
|
: undefined;
|
||||||
|
const changesInfo = getSuggestedChangesInfo(message, originalMessage);
|
||||||
|
|
||||||
|
const titleText = changesInfo
|
||||||
|
? getSuggestedChangesActionText(lang, message, originalMessage, message.isOutgoing, userLink)
|
||||||
|
: message.isOutgoing
|
||||||
|
? lang('ActionSuggestedPostOutgoing', undefined, { withNodes: true, withMarkdown: true })
|
||||||
|
: lang('ActionSuggestedPostIncoming', { user: userLink }, { withNodes: true, withMarkdown: true });
|
||||||
|
|
||||||
|
const tableData: TableEntry[] = compact([
|
||||||
|
price && [lang('TitlePrice'), formatStarsAsText(lang, price.amount)],
|
||||||
|
Boolean(scheduleDate) && [lang('TitleTime'), formatScheduledDateTime(scheduleDate, lang, oldLang)],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={buildClassName('local-action-message')}
|
||||||
|
key={`suggested-post-action-${message.id}`}
|
||||||
|
>
|
||||||
|
<span className={actionMessageStyles.suggestedPostContainer}>
|
||||||
|
<div
|
||||||
|
className={actionMessageStyles.suggestedPostTitle}
|
||||||
|
>
|
||||||
|
{titleText}
|
||||||
|
</div>
|
||||||
|
{Boolean(tableData.length) && (
|
||||||
|
<MiniTable
|
||||||
|
className={actionMessageStyles.suggestedPostInfo}
|
||||||
|
data={tableData}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
const messageCountToAnimate = noAppearanceAnimation ? 0 : messageGroups.reduce((acc, messageGroup) => {
|
const messageCountToAnimate = noAppearanceAnimation ? 0 : messageGroups.reduce((acc, messageGroup) => {
|
||||||
return acc + messageGroup.senderGroups.flat().length;
|
return acc + messageGroup.senderGroups.flat().length;
|
||||||
}, 0);
|
}, 0);
|
||||||
@ -268,6 +322,7 @@ const MessageListContent: FC<OwnProps> = ({
|
|||||||
return compact([
|
return compact([
|
||||||
message.id === memoUnreadDividerBeforeIdRef.current && unreadDivider,
|
message.id === memoUnreadDividerBeforeIdRef.current && unreadDivider,
|
||||||
message.paidMessageStars && !withUsers && renderPaidMessageAction(message, album),
|
message.paidMessageStars && !withUsers && renderPaidMessageAction(message, album),
|
||||||
|
message.suggestedPostInfo && renderSuggestedPostInfoAction(message),
|
||||||
<Message
|
<Message
|
||||||
key={key}
|
key={key}
|
||||||
message={message}
|
message={message}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
import { getActions, getGlobal, withGlobal } from '../../../global';
|
import { getActions, getGlobal, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiChat, ApiInputMessageReplyInfo, ApiMessage, ApiPeer,
|
ApiChat, ApiInputMessageReplyInfo, ApiInputSuggestedPostInfo, ApiMessage, ApiPeer,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import type { MessageListType, ThreadId } from '../../../types/index';
|
import type { MessageListType, ThreadId } from '../../../types/index';
|
||||||
|
|
||||||
@ -48,6 +48,7 @@ import './ComposerEmbeddedMessage.scss';
|
|||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
replyInfo?: ApiInputMessageReplyInfo;
|
replyInfo?: ApiInputMessageReplyInfo;
|
||||||
|
suggestedPostInfo?: ApiInputSuggestedPostInfo;
|
||||||
editingId?: number;
|
editingId?: number;
|
||||||
message?: ApiMessage;
|
message?: ApiMessage;
|
||||||
sender?: ApiPeer;
|
sender?: ApiPeer;
|
||||||
@ -80,6 +81,7 @@ const CLOSE_DURATION = 350;
|
|||||||
|
|
||||||
const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
||||||
replyInfo,
|
replyInfo,
|
||||||
|
suggestedPostInfo,
|
||||||
editingId,
|
editingId,
|
||||||
message,
|
message,
|
||||||
sender,
|
sender,
|
||||||
@ -104,6 +106,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
resetDraftReplyInfo,
|
resetDraftReplyInfo,
|
||||||
|
resetDraftSuggestedPostInfo,
|
||||||
updateDraftReplyInfo,
|
updateDraftReplyInfo,
|
||||||
setEditingId,
|
setEditingId,
|
||||||
focusMessage,
|
focusMessage,
|
||||||
@ -113,6 +116,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
setForwardNoCaptions,
|
setForwardNoCaptions,
|
||||||
exitForwardMode,
|
exitForwardMode,
|
||||||
setShouldPreventComposerAnimation,
|
setShouldPreventComposerAnimation,
|
||||||
|
openSuggestMessageModal,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
const ref = useRef<HTMLDivElement>();
|
const ref = useRef<HTMLDivElement>();
|
||||||
const oldLang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
@ -121,6 +125,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
const isReplyToTopicStart = message?.content.action?.type === 'topicCreate';
|
const isReplyToTopicStart = message?.content.action?.type === 'topicCreate';
|
||||||
const isShowingReply = replyInfo && !shouldForceShowEditing;
|
const isShowingReply = replyInfo && !shouldForceShowEditing;
|
||||||
const isReplyWithQuote = Boolean(replyInfo?.quoteText);
|
const isReplyWithQuote = Boolean(replyInfo?.quoteText);
|
||||||
|
const isShowingSuggestedPost = Boolean(suggestedPostInfo) && !shouldForceShowEditing;
|
||||||
|
|
||||||
const isForwarding = Boolean(forwardedMessagesCount);
|
const isForwarding = Boolean(forwardedMessagesCount);
|
||||||
|
|
||||||
@ -145,6 +150,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
if (isInChangingRecipientMode) return false;
|
if (isInChangingRecipientMode) return false;
|
||||||
if (message && (replyInfo || editingId)) return true;
|
if (message && (replyInfo || editingId)) return true;
|
||||||
if (forwardSenders && isForwarding) return true;
|
if (forwardSenders && isForwarding) return true;
|
||||||
|
if (isShowingSuggestedPost) return true;
|
||||||
return false;
|
return false;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -170,6 +176,9 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
setEditingId({ messageId: undefined });
|
setEditingId({ messageId: undefined });
|
||||||
} else if (forwardedMessagesCount) {
|
} else if (forwardedMessagesCount) {
|
||||||
exitForwardMode();
|
exitForwardMode();
|
||||||
|
} else if (isShowingSuggestedPost) {
|
||||||
|
resetDraftSuggestedPostInfo();
|
||||||
|
resetDraftReplyInfo();
|
||||||
} else if (replyInfo && !shouldForceShowEditing) {
|
} else if (replyInfo && !shouldForceShowEditing) {
|
||||||
resetDraftReplyInfo();
|
resetDraftReplyInfo();
|
||||||
}
|
}
|
||||||
@ -187,6 +196,10 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
focusMessage({ chatId: message!.chatId, messageId: message!.id, noForumTopicPanel: true });
|
focusMessage({ chatId: message!.chatId, messageId: message!.id, noForumTopicPanel: true });
|
||||||
};
|
};
|
||||||
const handleMessageClick = useLastCallback((e: React.MouseEvent): void => {
|
const handleMessageClick = useLastCallback((e: React.MouseEvent): void => {
|
||||||
|
if (suggestedPostInfo) {
|
||||||
|
openSuggestMessageModal({ chatId });
|
||||||
|
return;
|
||||||
|
}
|
||||||
handleContextMenu(e);
|
handleContextMenu(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -236,6 +249,9 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
if (editingId) {
|
if (editingId) {
|
||||||
return 'edit';
|
return 'edit';
|
||||||
}
|
}
|
||||||
|
if (isShowingSuggestedPost) {
|
||||||
|
return 'cash-circle';
|
||||||
|
}
|
||||||
if (isForwarding) {
|
if (isForwarding) {
|
||||||
return 'forward';
|
return 'forward';
|
||||||
}
|
}
|
||||||
@ -244,7 +260,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}, [editingId, isForwarding, isShowingReply]);
|
}, [editingId, isForwarding, isShowingReply, isShowingSuggestedPost]);
|
||||||
|
|
||||||
const customText = forwardedMessagesCount && forwardedMessagesCount > 1
|
const customText = forwardedMessagesCount && forwardedMessagesCount > 1
|
||||||
? oldLang('ForwardedMessageCount', forwardedMessagesCount)
|
? oldLang('ForwardedMessageCount', forwardedMessagesCount)
|
||||||
@ -284,6 +300,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
isOpen={isShown}
|
isOpen={isShown}
|
||||||
className="inside-input"
|
className="inside-input"
|
||||||
replyInfo={replyInfo}
|
replyInfo={replyInfo}
|
||||||
|
suggestedPostInfo={suggestedPostInfo}
|
||||||
isInComposer
|
isInComposer
|
||||||
message={strippedMessage}
|
message={strippedMessage}
|
||||||
sender={!noAuthors ? sender : undefined}
|
sender={!noAuthors ? sender : undefined}
|
||||||
@ -425,6 +442,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
|
|
||||||
const draft = selectDraft(global, chatId, threadId);
|
const draft = selectDraft(global, chatId, threadId);
|
||||||
const replyInfo = draft?.replyInfo;
|
const replyInfo = draft?.replyInfo;
|
||||||
|
const suggestedPostInfo = draft?.suggestedPostInfo;
|
||||||
const replyToPeerId = replyInfo?.replyToPeerId;
|
const replyToPeerId = replyInfo?.replyToPeerId;
|
||||||
const senderChat = replyToPeerId ? selectChat(global, replyToPeerId) : undefined;
|
const senderChat = replyToPeerId ? selectChat(global, replyToPeerId) : undefined;
|
||||||
|
|
||||||
@ -479,6 +497,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
replyInfo,
|
replyInfo,
|
||||||
|
suggestedPostInfo,
|
||||||
editingId,
|
editingId,
|
||||||
message,
|
message,
|
||||||
sender,
|
sender,
|
||||||
|
|||||||
@ -6,10 +6,11 @@ import type { ThreadId } from '../../../../types';
|
|||||||
import type { Signal } from '../../../../util/signals';
|
import type { Signal } from '../../../../util/signals';
|
||||||
import { ApiMessageEntityTypes } from '../../../../api/types';
|
import { ApiMessageEntityTypes } from '../../../../api/types';
|
||||||
|
|
||||||
import { DRAFT_DEBOUNCE } from '../../../../config';
|
import { DRAFT_DEBOUNCE, EDITABLE_INPUT_CSS_SELECTOR } from '../../../../config';
|
||||||
import {
|
import {
|
||||||
requestMeasure,
|
requestMeasure, requestNextMutation,
|
||||||
} from '../../../../lib/fasterdom/fasterdom';
|
} from '../../../../lib/fasterdom/fasterdom';
|
||||||
|
import focusEditableElement from '../../../../util/focusEditableElement';
|
||||||
import parseHtmlAsFormattedText from '../../../../util/parseHtmlAsFormattedText';
|
import parseHtmlAsFormattedText from '../../../../util/parseHtmlAsFormattedText';
|
||||||
import { getTextWithEntitiesAsHtml } from '../../../common/helpers/renderTextWithEntities';
|
import { getTextWithEntitiesAsHtml } from '../../../common/helpers/renderTextWithEntities';
|
||||||
|
|
||||||
@ -84,6 +85,7 @@ const useDraft = ({
|
|||||||
chatId: prevState.chatId ?? chatId,
|
chatId: prevState.chatId ?? chatId,
|
||||||
threadId: prevState.threadId ?? threadId,
|
threadId: prevState.threadId ?? threadId,
|
||||||
shouldKeepReply: true,
|
shouldKeepReply: true,
|
||||||
|
shouldKeepSuggestedPost: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -96,6 +98,7 @@ const useDraft = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isTouched = isTouchedRef.current;
|
const isTouched = isTouchedRef.current;
|
||||||
|
const shouldUpdateSuggestedPost = draft?.suggestedPostInfo && !prevDraft?.suggestedPostInfo;
|
||||||
|
|
||||||
if (chatId === prevChatId && threadId === prevThreadId) {
|
if (chatId === prevChatId && threadId === prevThreadId) {
|
||||||
if (isTouched && !draft) return; // Prevent reset from other client if we have local edits
|
if (isTouched && !draft) return; // Prevent reset from other client if we have local edits
|
||||||
@ -103,7 +106,7 @@ const useDraft = ({
|
|||||||
setHtml('');
|
setHtml('');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTouched) return;
|
if (isTouched && !shouldUpdateSuggestedPost) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editedMessage || !draft) {
|
if (editedMessage || !draft) {
|
||||||
@ -111,6 +114,14 @@ const useDraft = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setHtml(getTextWithEntitiesAsHtml(draft.text));
|
setHtml(getTextWithEntitiesAsHtml(draft.text));
|
||||||
|
if (shouldUpdateSuggestedPost) {
|
||||||
|
requestNextMutation(() => {
|
||||||
|
const messageInput = document.querySelector<HTMLDivElement>(EDITABLE_INPUT_CSS_SELECTOR);
|
||||||
|
if (messageInput) {
|
||||||
|
focusEditableElement(messageInput, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const customEmojiIds = draft.text?.entities
|
const customEmojiIds = draft.text?.entities
|
||||||
?.map((entity) => entity.type === ApiMessageEntityTypes.CustomEmoji && entity.documentId)
|
?.map((entity) => entity.type === ApiMessageEntityTypes.CustomEmoji && entity.documentId)
|
||||||
|
|||||||
@ -91,6 +91,7 @@ export function groupMessages(
|
|||||||
nextMessage.id === firstUnreadId
|
nextMessage.id === firstUnreadId
|
||||||
|| message.senderId !== nextMessage.senderId
|
|| message.senderId !== nextMessage.senderId
|
||||||
|| (!withUsers && message.paidMessageStars)
|
|| (!withUsers && message.paidMessageStars)
|
||||||
|
|| (nextMessage.suggestedPostInfo)
|
||||||
|| message.isOutgoing !== nextMessage.isOutgoing
|
|| message.isOutgoing !== nextMessage.isOutgoing
|
||||||
|| message.postAuthorTitle !== nextMessage.postAuthorTitle
|
|| message.postAuthorTitle !== nextMessage.postAuthorTitle
|
||||||
|| (isActionMessage(message) && message.content.action?.type !== 'phoneCall')
|
|| (isActionMessage(message) && message.content.action?.type !== 'phoneCall')
|
||||||
|
|||||||
@ -201,3 +201,84 @@
|
|||||||
.uniqueValue {
|
.uniqueValue {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.suggestedPostContainer {
|
||||||
|
padding: 0.5rem 0.75rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestedPostTitle {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 10rem;
|
||||||
|
font-weight: var(--font-weight-normal);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestedPostInfo {
|
||||||
|
width: fit-content;
|
||||||
|
margin: 0.75rem auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestedPostBalanceTooLowBox,
|
||||||
|
.suggestedPostRejectedContentBox,
|
||||||
|
.suggestedPostContentBox,
|
||||||
|
.hoverable .textContent {
|
||||||
|
cursor: var(--custom-cursor, pointer);
|
||||||
|
transition: opacity 0.15s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestedPostContentBox {
|
||||||
|
max-width: 20rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestedPostBalanceTooLowBox {
|
||||||
|
max-width: 20rem !important;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestedPostBalanceTooLowTitle {
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestedPostRejectedContentBox {
|
||||||
|
max-width: 15rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestedPostApprovalTitle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0.375rem;
|
||||||
|
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestedPostApprovalSection {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0.375rem;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestedPostRejectedComment {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestedPostRejectedTitle {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rejectedIcon {
|
||||||
|
margin-right: 0.125rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import type React from '../../../lib/teact/teact';
|
|
||||||
import {
|
import {
|
||||||
memo, useEffect, useMemo, useRef, useUnmountCleanup,
|
memo, useEffect, useMemo, useRef, useUnmountCleanup,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
@ -53,6 +52,9 @@ import GiveawayPrize from './actions/GiveawayPrize';
|
|||||||
import StarGift from './actions/StarGift';
|
import StarGift from './actions/StarGift';
|
||||||
import StarGiftUnique from './actions/StarGiftUnique';
|
import StarGiftUnique from './actions/StarGiftUnique';
|
||||||
import SuggestedPhoto from './actions/SuggestedPhoto';
|
import SuggestedPhoto from './actions/SuggestedPhoto';
|
||||||
|
import SuggestedPostApproval from './actions/SuggestedPostApproval';
|
||||||
|
import SuggestedPostBalanceTooLow from './actions/SuggestedPostBalanceTooLow';
|
||||||
|
import SuggestedPostRejected from './actions/SuggestedPostRejected';
|
||||||
import ContextMenuContainer from './ContextMenuContainer';
|
import ContextMenuContainer from './ContextMenuContainer';
|
||||||
import Reactions from './reactions/Reactions';
|
import Reactions from './reactions/Reactions';
|
||||||
import SimilarChannels from './SimilarChannels';
|
import SimilarChannels from './SimilarChannels';
|
||||||
@ -98,7 +100,8 @@ const SINGLE_LINE_ACTIONS = new Set<ApiMessageAction['type']>([
|
|||||||
'todoAppendTasks',
|
'todoAppendTasks',
|
||||||
'unsupported',
|
'unsupported',
|
||||||
]);
|
]);
|
||||||
const HIDDEN_TEXT_ACTIONS = new Set<ApiMessageAction['type']>(['giftCode', 'prizeStars', 'suggestProfilePhoto']);
|
const HIDDEN_TEXT_ACTIONS = new Set<ApiMessageAction['type']>(['giftCode', 'prizeStars',
|
||||||
|
'suggestProfilePhoto', 'suggestedPostApproval']);
|
||||||
|
|
||||||
const ActionMessage = ({
|
const ActionMessage = ({
|
||||||
message,
|
message,
|
||||||
@ -139,6 +142,7 @@ const ActionMessage = ({
|
|||||||
toggleChannelRecommendations,
|
toggleChannelRecommendations,
|
||||||
animateUnreadReaction,
|
animateUnreadReaction,
|
||||||
markMentionsRead,
|
markMentionsRead,
|
||||||
|
focusMessage,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const ref = useRef<HTMLDivElement>();
|
const ref = useRef<HTMLDivElement>();
|
||||||
@ -150,6 +154,7 @@ const ActionMessage = ({
|
|||||||
const isTextHidden = HIDDEN_TEXT_ACTIONS.has(action.type);
|
const isTextHidden = HIDDEN_TEXT_ACTIONS.has(action.type);
|
||||||
const isSingleLine = SINGLE_LINE_ACTIONS.has(action.type);
|
const isSingleLine = SINGLE_LINE_ACTIONS.has(action.type);
|
||||||
const isFluidMultiline = IS_FLUID_BACKGROUND_SUPPORTED && !isSingleLine;
|
const isFluidMultiline = IS_FLUID_BACKGROUND_SUPPORTED && !isSingleLine;
|
||||||
|
const isClickableText = action.type === 'suggestedPostSuccess';
|
||||||
|
|
||||||
const messageReplyInfo = getMessageReplyInfo(message);
|
const messageReplyInfo = getMessageReplyInfo(message);
|
||||||
const { replyToMsgId, replyToPeerId } = messageReplyInfo || {};
|
const { replyToMsgId, replyToPeerId } = messageReplyInfo || {};
|
||||||
@ -310,6 +315,30 @@ const ActionMessage = ({
|
|||||||
toggleChannelRecommendations({ chatId });
|
toggleChannelRecommendations({ chatId });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'suggestedPostApproval': {
|
||||||
|
const replyInfo = getMessageReplyInfo(message);
|
||||||
|
if (replyInfo?.type === 'message' && replyInfo.replyToMsgId) {
|
||||||
|
focusMessage({
|
||||||
|
chatId: message.chatId,
|
||||||
|
threadId,
|
||||||
|
messageId: replyInfo.replyToMsgId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'suggestedPostSuccess': {
|
||||||
|
const replyInfo = getMessageReplyInfo(message);
|
||||||
|
if (replyInfo?.type === 'message' && replyInfo.replyToMsgId) {
|
||||||
|
focusMessage({
|
||||||
|
chatId: message.chatId,
|
||||||
|
threadId,
|
||||||
|
messageId: replyInfo.replyToMsgId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -387,6 +416,30 @@ const ActionMessage = ({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
case 'suggestedPostApproval':
|
||||||
|
if (action.isBalanceTooLow) {
|
||||||
|
return (
|
||||||
|
<SuggestedPostBalanceTooLow
|
||||||
|
message={message}
|
||||||
|
action={action}
|
||||||
|
onClick={handleClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return action.isRejected ? (
|
||||||
|
<SuggestedPostRejected
|
||||||
|
message={message}
|
||||||
|
action={action}
|
||||||
|
onClick={handleClick}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<SuggestedPostApproval
|
||||||
|
message={message}
|
||||||
|
action={action}
|
||||||
|
onClick={handleClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -421,13 +474,13 @@ const ActionMessage = ({
|
|||||||
{!isTextHidden && (
|
{!isTextHidden && (
|
||||||
<>
|
<>
|
||||||
{isFluidMultiline && (
|
{isFluidMultiline && (
|
||||||
<div className={styles.inlineWrapper}>
|
<div className={buildClassName(styles.inlineWrapper, isClickableText && styles.hoverable)}>
|
||||||
<span className={styles.fluidBackground} style={fluidBackgroundStyle}>
|
<span className={styles.fluidBackground} style={fluidBackgroundStyle}>
|
||||||
<ActionMessageText message={message} isInsideTopic={isInsideTopic} />
|
<ActionMessageText message={message} isInsideTopic={isInsideTopic} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={styles.inlineWrapper}>
|
<div className={buildClassName(styles.inlineWrapper, isClickableText && styles.hoverable)}>
|
||||||
<span className={styles.textContent} onClick={handleClick}>
|
<span className={styles.textContent} onClick={handleClick}>
|
||||||
<ActionMessageText message={message} isInsideTopic={isInsideTopic} />
|
<ActionMessageText message={message} isInsideTopic={isInsideTopic} />
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -18,16 +18,18 @@ import { getMessageReplyInfo } from '../../../global/helpers/replies';
|
|||||||
import {
|
import {
|
||||||
selectChat,
|
selectChat,
|
||||||
selectChatMessage,
|
selectChatMessage,
|
||||||
|
selectMonoforumChannel,
|
||||||
selectPeer,
|
selectPeer,
|
||||||
selectSender,
|
selectSender,
|
||||||
selectThreadIdFromMessage,
|
selectThreadIdFromMessage,
|
||||||
selectTopic,
|
selectTopic,
|
||||||
} from '../../../global/selectors';
|
} from '../../../global/selectors';
|
||||||
import { ensureProtocol } from '../../../util/browser/url';
|
import { ensureProtocol } from '../../../util/browser/url';
|
||||||
import { formatDateTimeToString, formatShortDuration } from '../../../util/dates/dateFormat';
|
import { formatDateTimeToString, formatScheduledDateTime, formatShortDuration } from '../../../util/dates/dateFormat';
|
||||||
import { formatCurrency } from '../../../util/formatCurrency';
|
import { formatCurrency } from '../../../util/formatCurrency';
|
||||||
import { formatStarsAsText } from '../../../util/localization/format';
|
import { formatStarsAsText } from '../../../util/localization/format';
|
||||||
import { conjuctionWithNodes } from '../../../util/localization/utils';
|
import { conjuctionWithNodes } from '../../../util/localization/utils';
|
||||||
|
import { getServerTime } from '../../../util/serverTime';
|
||||||
import renderText from '../../common/helpers/renderText';
|
import renderText from '../../common/helpers/renderText';
|
||||||
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
|
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
|
||||||
import {
|
import {
|
||||||
@ -40,6 +42,7 @@ import {
|
|||||||
|
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
import CustomEmoji from '../../common/CustomEmoji';
|
import CustomEmoji from '../../common/CustomEmoji';
|
||||||
import TopicDefaultIcon from '../../common/TopicDefaultIcon';
|
import TopicDefaultIcon from '../../common/TopicDefaultIcon';
|
||||||
@ -83,6 +86,7 @@ const ActionMessageText = ({
|
|||||||
const action = message.content.action!;
|
const action = message.content.action!;
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
const oldLang = useOldLang();
|
||||||
|
|
||||||
function renderStrong(text: TeactNode) {
|
function renderStrong(text: TeactNode) {
|
||||||
if (asPreview) return text;
|
if (asPreview) return text;
|
||||||
@ -92,10 +96,10 @@ const ActionMessageText = ({
|
|||||||
const renderActionText = useLastCallback(() => {
|
const renderActionText = useLastCallback(() => {
|
||||||
const global = getGlobal();
|
const global = getGlobal();
|
||||||
|
|
||||||
const isChannel = chat && isChatChannel(chat);
|
|
||||||
const isServiceNotificationsChat = chatId === SERVICE_NOTIFICATIONS_USER_ID;
|
const isServiceNotificationsChat = chatId === SERVICE_NOTIFICATIONS_USER_ID;
|
||||||
const isSavedMessages = chatId === currentUserId;
|
const isSavedMessages = chatId === currentUserId;
|
||||||
|
|
||||||
|
const isChannel = chat && isChatChannel(chat);
|
||||||
const senderTitle = sender && getPeerTitle(lang, sender);
|
const senderTitle = sender && getPeerTitle(lang, sender);
|
||||||
const chatTitle = chat && getPeerTitle(lang, chat);
|
const chatTitle = chat && getPeerTitle(lang, chat);
|
||||||
|
|
||||||
@ -750,6 +754,97 @@ const ActionMessageText = ({
|
|||||||
}, { withNodes: true, withMarkdown: true });
|
}, { withNodes: true, withMarkdown: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'suggestedPostSuccess': {
|
||||||
|
const { amount: stars } = action;
|
||||||
|
const channel = chat?.isMonoforum ? selectMonoforumChannel(global, chatId) : chat;
|
||||||
|
const channelTitle = channel && getPeerTitle(lang, channel);
|
||||||
|
const channelLink = renderPeerLink(channel?.id, channelTitle || channelFallbackText, asPreview);
|
||||||
|
return lang('ActionSuggestedPostSuccess', {
|
||||||
|
channel: channelLink,
|
||||||
|
amount: formatStarsAsText(lang, stars?.amount || 0),
|
||||||
|
}, { withNodes: true });
|
||||||
|
}
|
||||||
|
case 'suggestedPostRefund': {
|
||||||
|
const { payerInitiated } = action;
|
||||||
|
|
||||||
|
const replyMessage = message.replyInfo?.type === 'message' && message.replyInfo.replyToMsgId
|
||||||
|
? selectChatMessage(global, chatId, message.replyInfo.replyToMsgId)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const postSender = replyMessage ? selectSender(global, replyMessage) : sender;
|
||||||
|
const postSenderTitle = postSender && getPeerTitle(lang, postSender);
|
||||||
|
const postSenderLink = renderPeerLink(postSender?.id, postSenderTitle || userFallbackText, asPreview);
|
||||||
|
|
||||||
|
const starsAmount = replyMessage?.suggestedPostInfo?.price?.amount || 0;
|
||||||
|
|
||||||
|
const channel = chat?.isMonoforum ? selectMonoforumChannel(global, chatId) : chat;
|
||||||
|
const channelTitle = channel && getPeerTitle(lang, channel);
|
||||||
|
const channelLink = renderPeerLink(channel?.id, channelTitle || channelFallbackText, asPreview);
|
||||||
|
|
||||||
|
if (payerInitiated) {
|
||||||
|
return lang('SuggestedPostRefundedByUser', {
|
||||||
|
amount: formatStarsAsText(lang, starsAmount),
|
||||||
|
user: postSenderLink,
|
||||||
|
channel: channelLink,
|
||||||
|
}, { withNodes: true, withMarkdown: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return lang('SuggestedPostRefundedByChannel', {
|
||||||
|
amount: formatStarsAsText(lang, starsAmount),
|
||||||
|
peer: postSenderLink,
|
||||||
|
channel: channelLink,
|
||||||
|
}, { withNodes: true, withMarkdown: true });
|
||||||
|
}
|
||||||
|
case 'suggestedPostApproval': {
|
||||||
|
const { isRejected, isBalanceTooLow, rejectComment } = action;
|
||||||
|
|
||||||
|
if (isRejected) {
|
||||||
|
const senderTitle = sender && getPeerTitle(lang, sender);
|
||||||
|
const senderLink = renderPeerLink(sender?.id, senderTitle || userFallbackText, asPreview);
|
||||||
|
|
||||||
|
return translateWithYou(
|
||||||
|
lang,
|
||||||
|
rejectComment ? 'SuggestedPostRejectedWithReason' : 'SuggestedPostRejected',
|
||||||
|
isOutgoing,
|
||||||
|
{ peer: senderLink },
|
||||||
|
{ withMarkdown: true },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBalanceTooLow) {
|
||||||
|
const replyMessage = message.replyInfo?.type === 'message' && message.replyInfo.replyToMsgId
|
||||||
|
? selectChatMessage(global, chatId, message.replyInfo.replyToMsgId)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const replyMessageSender = replyMessage ? selectSender(global, replyMessage) : sender;
|
||||||
|
const replyPeerTitle = replyMessageSender && getPeerTitle(lang, replyMessageSender);
|
||||||
|
const userLink = renderPeerLink(replyMessageSender?.id, replyPeerTitle || userFallbackText, asPreview);
|
||||||
|
return lang('SuggestedPostBalanceTooLow', {
|
||||||
|
peer: userLink,
|
||||||
|
currency: lang('CurrencyStars'),
|
||||||
|
}, { withNodes: true, withMarkdown: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const channel = chat?.isMonoforum ? selectMonoforumChannel(global, chatId) : chat;
|
||||||
|
const channelTitle = channel && getPeerTitle(lang, channel);
|
||||||
|
const channelLink = renderPeerLink(channel?.id, channelTitle || channelFallbackText, asPreview);
|
||||||
|
|
||||||
|
const { scheduleDate } = action;
|
||||||
|
if (scheduleDate) {
|
||||||
|
const publishDate = formatScheduledDateTime(scheduleDate, lang, oldLang);
|
||||||
|
const isPostPublished = scheduleDate <= getServerTime();
|
||||||
|
|
||||||
|
return translateWithYou(
|
||||||
|
lang,
|
||||||
|
isPostPublished ? 'SuggestedPostPublished' : 'SuggestedPostPublishSchedule',
|
||||||
|
isOutgoing,
|
||||||
|
{ peer: channelLink, date: publishDate },
|
||||||
|
{ withMarkdown: true },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lang(UNSUPPORTED_LANG_KEY);
|
||||||
|
}
|
||||||
case 'todoCompletions': {
|
case 'todoCompletions': {
|
||||||
const { completedIds, incompletedIds } = action;
|
const { completedIds, incompletedIds } = action;
|
||||||
|
|
||||||
|
|||||||
@ -59,6 +59,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.left-icon {
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
.inline-button-text {
|
.inline-button-text {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|||||||
@ -43,6 +43,17 @@ const InlineButtons: FC<OwnProps> = ({ message, onClick }) => {
|
|||||||
return <Icon className="corner-icon" name="webapp" />;
|
return <Icon className="corner-icon" name="webapp" />;
|
||||||
case 'copy':
|
case 'copy':
|
||||||
return <Icon className="corner-icon" name="copy" />;
|
return <Icon className="corner-icon" name="copy" />;
|
||||||
|
case 'suggestedMessage':
|
||||||
|
if (button.buttonType === 'suggestChanges') {
|
||||||
|
return <Icon className="left-icon" name="edit" />;
|
||||||
|
}
|
||||||
|
if (button.buttonType === 'approve') {
|
||||||
|
return <Icon className="left-icon" name="check" />;
|
||||||
|
}
|
||||||
|
if (button.buttonType === 'decline') {
|
||||||
|
return <Icon className="left-icon" name="close" />;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
@ -63,14 +74,14 @@ const InlineButtons: FC<OwnProps> = ({ message, onClick }) => {
|
|||||||
<Button
|
<Button
|
||||||
size="tiny"
|
size="tiny"
|
||||||
ripple
|
ripple
|
||||||
disabled={button.type === 'unsupported'}
|
disabled={button.type === 'unsupported' || (button.type === 'suggestedMessage' && button.disabled)}
|
||||||
|
|
||||||
onClick={() => onClick({ chatId: message.chatId, messageId: message.id, button })}
|
onClick={() => onClick({ chatId: message.chatId, messageId: message.id, button })}
|
||||||
>
|
>
|
||||||
|
{renderIcon(button)}
|
||||||
<span className="inline-button-text">
|
<span className="inline-button-text">
|
||||||
{buttonTexts[i][j]}
|
{buttonTexts[i][j]}
|
||||||
</span>
|
</span>
|
||||||
{renderIcon(button)}
|
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import type {
|
|||||||
ApiTypeStory,
|
ApiTypeStory,
|
||||||
ApiUser,
|
ApiUser,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
|
import type { ActionPayloads } from '../../../global/types';
|
||||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||||
import type {
|
import type {
|
||||||
ActiveEmojiInteraction,
|
ActiveEmojiInteraction,
|
||||||
@ -44,7 +45,7 @@ import type { OnIntersectPinnedMessage } from '../hooks/usePinnedMessage';
|
|||||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||||
import { AudioOrigin } from '../../../types';
|
import { AudioOrigin } from '../../../types';
|
||||||
|
|
||||||
import { EMOJI_STATUS_LOOP_LIMIT, MESSAGE_APPEARANCE_DELAY } from '../../../config';
|
import { EMOJI_STATUS_LOOP_LIMIT, MESSAGE_APPEARANCE_DELAY, STARS_SUGGESTED_POST_FUTURE_MIN } from '../../../config';
|
||||||
import {
|
import {
|
||||||
areReactionsEmpty,
|
areReactionsEmpty,
|
||||||
getIsDownloading,
|
getIsDownloading,
|
||||||
@ -120,6 +121,7 @@ import { IS_ANDROID, IS_ELECTRON, IS_TRANSLATION_SUPPORTED } from '../../../util
|
|||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { isUserId } from '../../../util/entities/ids';
|
import { isUserId } from '../../../util/entities/ids';
|
||||||
import { getMessageKey } from '../../../util/keys/messageKey';
|
import { getMessageKey } from '../../../util/keys/messageKey';
|
||||||
|
import { getServerTime } from '../../../util/serverTime';
|
||||||
import stopEvent from '../../../util/stopEvent';
|
import stopEvent from '../../../util/stopEvent';
|
||||||
import { isElementInViewport } from '../../../util/visibility/isElementInViewport';
|
import { isElementInViewport } from '../../../util/visibility/isElementInViewport';
|
||||||
import { calculateDimensionsForMessageMedia, getStickerDimensions, REM } from '../../common/helpers/mediaDimensions';
|
import { calculateDimensionsForMessageMedia, getStickerDimensions, REM } from '../../common/helpers/mediaDimensions';
|
||||||
@ -137,6 +139,7 @@ import useEnsureMessage from '../../../hooks/useEnsureMessage';
|
|||||||
import useEnsureStory from '../../../hooks/useEnsureStory';
|
import useEnsureStory from '../../../hooks/useEnsureStory';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import { useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
import { useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
import useLastCallback from '../../../hooks/useLastCallback';
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
import useOldLang from '../../../hooks/useOldLang';
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
import usePreviousDeprecated from '../../../hooks/usePreviousDeprecated';
|
import usePreviousDeprecated from '../../../hooks/usePreviousDeprecated';
|
||||||
@ -164,6 +167,8 @@ import ReactionStaticEmoji from '../../common/reactions/ReactionStaticEmoji';
|
|||||||
import TopicChip from '../../common/TopicChip';
|
import TopicChip from '../../common/TopicChip';
|
||||||
import { animateSnap } from '../../main/visualEffects/SnapEffectContainer';
|
import { animateSnap } from '../../main/visualEffects/SnapEffectContainer';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
|
import InputText from '../../ui/InputText';
|
||||||
import Album from './Album';
|
import Album from './Album';
|
||||||
import AnimatedCustomEmoji from './AnimatedCustomEmoji';
|
import AnimatedCustomEmoji from './AnimatedCustomEmoji';
|
||||||
import AnimatedEmoji from './AnimatedEmoji';
|
import AnimatedEmoji from './AnimatedEmoji';
|
||||||
@ -310,6 +315,7 @@ type StateProps = {
|
|||||||
paidMessageStars?: number;
|
paidMessageStars?: number;
|
||||||
isChatWithUser?: boolean;
|
isChatWithUser?: boolean;
|
||||||
isAccountFrozen?: boolean;
|
isAccountFrozen?: boolean;
|
||||||
|
minFutureTime?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type MetaPosition =
|
type MetaPosition =
|
||||||
@ -328,6 +334,7 @@ const NBSP = '\u00A0';
|
|||||||
const NO_MEDIA_CORNERS_THRESHOLD = 18;
|
const NO_MEDIA_CORNERS_THRESHOLD = 18;
|
||||||
const QUICK_REACTION_SIZE = 1.75 * REM;
|
const QUICK_REACTION_SIZE = 1.75 * REM;
|
||||||
const EXTRA_SPACE_FOR_REACTIONS = 2.25 * REM;
|
const EXTRA_SPACE_FOR_REACTIONS = 2.25 * REM;
|
||||||
|
const MAX_REASON_LENGTH = 200;
|
||||||
|
|
||||||
const Message: FC<OwnProps & StateProps> = ({
|
const Message: FC<OwnProps & StateProps> = ({
|
||||||
message,
|
message,
|
||||||
@ -434,10 +441,14 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
paidMessageStars,
|
paidMessageStars,
|
||||||
isChatWithUser,
|
isChatWithUser,
|
||||||
isAccountFrozen,
|
isAccountFrozen,
|
||||||
|
minFutureTime,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
toggleMessageSelection,
|
toggleMessageSelection,
|
||||||
clickBotInlineButton,
|
clickBotInlineButton,
|
||||||
|
clickSuggestedMessageButton,
|
||||||
|
rejectSuggestedPost,
|
||||||
|
openSuggestedPostApprovalModal,
|
||||||
disableContextMenuHint,
|
disableContextMenuHint,
|
||||||
animateUnreadReaction,
|
animateUnreadReaction,
|
||||||
focusLastMessage,
|
focusLastMessage,
|
||||||
@ -448,12 +459,15 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
const bottomMarkerRef = useRef<HTMLDivElement>();
|
const bottomMarkerRef = useRef<HTMLDivElement>();
|
||||||
const quickReactionRef = useRef<HTMLDivElement>();
|
const quickReactionRef = useRef<HTMLDivElement>();
|
||||||
|
|
||||||
const lang = useOldLang();
|
const oldLang = useOldLang();
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
const [isTranscriptionHidden, setTranscriptionHidden] = useState(false);
|
const [isTranscriptionHidden, setTranscriptionHidden] = useState(false);
|
||||||
const [isPlayingSnapAnimation, setIsPlayingSnapAnimation] = useState(false);
|
const [isPlayingSnapAnimation, setIsPlayingSnapAnimation] = useState(false);
|
||||||
const [isPlayingDeleteAnimation, setIsPlayingDeleteAnimation] = useState(false);
|
const [isPlayingDeleteAnimation, setIsPlayingDeleteAnimation] = useState(false);
|
||||||
const [shouldPlayEffect, requestEffect, hideEffect] = useFlag();
|
const [shouldPlayEffect, requestEffect, hideEffect] = useFlag();
|
||||||
|
const [isDeclineDialogOpen, openDeclineDialog, closeDeclineDialog] = useFlag();
|
||||||
|
const [declineReason, setDeclineReason] = useState('');
|
||||||
const { isMobile, isTouchScreen } = useAppLayout();
|
const { isMobile, isTouchScreen } = useAppLayout();
|
||||||
|
|
||||||
useOnIntersect(bottomMarkerRef, observeIntersectionForBottom);
|
useOnIntersect(bottomMarkerRef, observeIntersectionForBottom);
|
||||||
@ -646,7 +660,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
handleTopicChipClick,
|
handleTopicChipClick,
|
||||||
handleStoryClick,
|
handleStoryClick,
|
||||||
} = useInnerHandlers({
|
} = useInnerHandlers({
|
||||||
lang,
|
lang: oldLang,
|
||||||
selectMessage,
|
selectMessage,
|
||||||
message,
|
message,
|
||||||
chatId,
|
chatId,
|
||||||
@ -857,7 +871,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
scrollTargetPosition,
|
scrollTargetPosition,
|
||||||
});
|
});
|
||||||
|
|
||||||
const viaBusinessBotTitle = viaBusinessBot ? getPeerFullTitle(lang, viaBusinessBot) : undefined;
|
const viaBusinessBotTitle = viaBusinessBot ? getPeerFullTitle(oldLang, viaBusinessBot) : undefined;
|
||||||
|
|
||||||
const canShowPostAuthor = !message.senderId;
|
const canShowPostAuthor = !message.senderId;
|
||||||
const signature = viaBusinessBotTitle || (canShowPostAuthor && message.postAuthorTitle)
|
const signature = viaBusinessBotTitle || (canShowPostAuthor && message.postAuthorTitle)
|
||||||
@ -1269,7 +1283,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
dir="auto"
|
dir="auto"
|
||||||
>
|
>
|
||||||
{(isTranscriptionError ? lang('NoWordsRecognized') : (
|
{(isTranscriptionError ? oldLang('NoWordsRecognized') : (
|
||||||
isTranscribing && transcribedText ? <DotAnimation content={transcribedText} /> : transcribedText
|
isTranscribing && transcribedText ? <DotAnimation content={transcribedText} /> : transcribedText
|
||||||
))}
|
))}
|
||||||
</p>
|
</p>
|
||||||
@ -1482,24 +1496,59 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
{asForwarded && (
|
{asForwarded && (
|
||||||
<span className="forward-title">
|
<span className="forward-title">
|
||||||
{lang('ForwardedFrom')}
|
{oldLang('ForwardedFrom')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSuggestedMessageButton = useLastCallback((payload: ActionPayloads['clickBotInlineButton']) => {
|
||||||
|
if (payload.button.type !== 'suggestedMessage') return;
|
||||||
|
if (payload.button.buttonType === 'approve') {
|
||||||
|
openSuggestedPostApprovalModal({
|
||||||
|
chatId,
|
||||||
|
messageId: message.id,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.button.buttonType === 'decline') {
|
||||||
|
openDeclineDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clickSuggestedMessageButton({
|
||||||
|
...payload,
|
||||||
|
button: payload.button,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleDeclineReasonChange = useLastCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setDeclineReason(e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleDeclineConfirm = useLastCallback(() => {
|
||||||
|
rejectSuggestedPost({
|
||||||
|
chatId,
|
||||||
|
messageId: message.id,
|
||||||
|
rejectComment: declineReason.trim() || undefined,
|
||||||
|
});
|
||||||
|
closeDeclineDialog();
|
||||||
|
setDeclineReason('');
|
||||||
|
});
|
||||||
|
|
||||||
function renderSenderName(
|
function renderSenderName(
|
||||||
shouldSkipRenderForwardTitle: boolean = false, shouldSkipRenderAdminTitle: boolean = false,
|
shouldSkipRenderForwardTitle: boolean = false, shouldSkipRenderAdminTitle: boolean = false,
|
||||||
) {
|
) {
|
||||||
let senderTitle;
|
let senderTitle;
|
||||||
let senderColor;
|
let senderColor;
|
||||||
if (senderPeer && !(isCustomShape && viaBotId)) {
|
if (senderPeer && !(isCustomShape && viaBotId)) {
|
||||||
senderTitle = getPeerFullTitle(lang, senderPeer);
|
senderTitle = getPeerFullTitle(oldLang, senderPeer);
|
||||||
} else if (forwardInfo?.hiddenUserName) {
|
} else if (forwardInfo?.hiddenUserName) {
|
||||||
senderTitle = forwardInfo.hiddenUserName;
|
senderTitle = forwardInfo.hiddenUserName;
|
||||||
} else if (storyData && originSender) {
|
} else if (storyData && originSender) {
|
||||||
senderTitle = getPeerFullTitle(lang, originSender);
|
senderTitle = getPeerFullTitle(oldLang, originSender);
|
||||||
}
|
}
|
||||||
const senderEmojiStatus = senderPeer && 'emojiStatus' in senderPeer && senderPeer.emojiStatus;
|
const senderEmojiStatus = senderPeer && 'emojiStatus' in senderPeer && senderPeer.emojiStatus;
|
||||||
const senderIsPremium = senderPeer && 'isPremium' in senderPeer && senderPeer.isPremium;
|
const senderIsPremium = senderPeer && 'isPremium' in senderPeer && senderPeer.isPremium;
|
||||||
@ -1551,7 +1600,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
) : undefined}
|
) : undefined}
|
||||||
{botSender?.hasUsername && (
|
{botSender?.hasUsername && (
|
||||||
<span className="interactive">
|
<span className="interactive">
|
||||||
<span className="via">{lang('ViaBot')}</span>
|
<span className="via">{oldLang('ViaBot')}</span>
|
||||||
<span
|
<span
|
||||||
className="sender-title"
|
className="sender-title"
|
||||||
onClick={handleViaBotClick}
|
onClick={handleViaBotClick}
|
||||||
@ -1562,12 +1611,12 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
<div className="title-spacer" />
|
<div className="title-spacer" />
|
||||||
{!shouldSkipRenderAdminTitle && !hasBotSenderUsername ? (forwardInfo?.isLinkedChannelPost ? (
|
{!shouldSkipRenderAdminTitle && !hasBotSenderUsername ? (forwardInfo?.isLinkedChannelPost ? (
|
||||||
<span className="admin-title" dir="auto">{lang('DiscussChannel')}</span>
|
<span className="admin-title" dir="auto">{oldLang('DiscussChannel')}</span>
|
||||||
) : message.postAuthorTitle && isGroup && !asForwarded ? (
|
) : message.postAuthorTitle && isGroup && !asForwarded ? (
|
||||||
<span className="admin-title" dir="auto">{message.postAuthorTitle}</span>
|
<span className="admin-title" dir="auto">{message.postAuthorTitle}</span>
|
||||||
) : senderAdminMember && !asForwarded && !viaBotId ? (
|
) : senderAdminMember && !asForwarded && !viaBotId ? (
|
||||||
<span className="admin-title" dir="auto">
|
<span className="admin-title" dir="auto">
|
||||||
{senderAdminMember.customTitle || lang(
|
{senderAdminMember.customTitle || oldLang(
|
||||||
senderAdminMember.isOwner ? 'GroupInfo.LabelOwner' : 'GroupInfo.LabelAdmin',
|
senderAdminMember.isOwner ? 'GroupInfo.LabelOwner' : 'GroupInfo.LabelAdmin',
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
@ -1583,6 +1632,14 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const forwardAuthor = isGroup && asForwarded ? message.postAuthorTitle : undefined;
|
const forwardAuthor = isGroup && asForwarded ? message.postAuthorTitle : undefined;
|
||||||
|
const shouldRenderSuggestedPostButtons = message.suggestedPostInfo
|
||||||
|
&& !message.isOutgoing && !message.suggestedPostInfo.isAccepted && !message.suggestedPostInfo.isRejected;
|
||||||
|
|
||||||
|
const isSuggestedPostExpired = useMemo(() => {
|
||||||
|
if (!message.suggestedPostInfo?.scheduleDate || !minFutureTime) return false;
|
||||||
|
const now = getServerTime();
|
||||||
|
return message.suggestedPostInfo.scheduleDate <= now + minFutureTime;
|
||||||
|
}, [message.suggestedPostInfo, minFutureTime]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -1666,7 +1723,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
color="translucent-white"
|
color="translucent-white"
|
||||||
round
|
round
|
||||||
size="tiny"
|
size="tiny"
|
||||||
ariaLabel={lang('lng_context_forward_msg')}
|
ariaLabel={oldLang('lng_context_forward_msg')}
|
||||||
onClick={isLastInDocumentGroup ? handleGroupForward : handleForward}
|
onClick={isLastInDocumentGroup ? handleGroupForward : handleForward}
|
||||||
>
|
>
|
||||||
<Icon name="share-filled" />
|
<Icon name="share-filled" />
|
||||||
@ -1699,6 +1756,36 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
{message.inlineButtons && (
|
{message.inlineButtons && (
|
||||||
<InlineButtons message={message} onClick={clickBotInlineButton} />
|
<InlineButtons message={message} onClick={clickBotInlineButton} />
|
||||||
)}
|
)}
|
||||||
|
{shouldRenderSuggestedPostButtons && (
|
||||||
|
<InlineButtons
|
||||||
|
message={{
|
||||||
|
...message,
|
||||||
|
inlineButtons: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: 'suggestedMessage',
|
||||||
|
buttonType: 'decline',
|
||||||
|
text: lang('SuggestedPostDecline'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'suggestedMessage',
|
||||||
|
buttonType: 'approve',
|
||||||
|
text: lang('SuggestedPostApprove'),
|
||||||
|
disabled: isSuggestedPostExpired,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: 'suggestedMessage',
|
||||||
|
buttonType: 'suggestChanges',
|
||||||
|
text: lang('SuggestedPostSuggestChanges'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
onClick={handleSuggestedMessageButton}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{reactionsPosition === 'outside' && !isStoryMention && (
|
{reactionsPosition === 'outside' && !isStoryMention && (
|
||||||
<Reactions
|
<Reactions
|
||||||
message={reactionMessage!}
|
message={reactionMessage!}
|
||||||
@ -1728,6 +1815,28 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
detectedLanguage={detectedLanguage}
|
detectedLanguage={detectedLanguage}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{isDeclineDialogOpen && (
|
||||||
|
<ConfirmDialog
|
||||||
|
isOpen={isDeclineDialogOpen}
|
||||||
|
onClose={closeDeclineDialog}
|
||||||
|
title={lang('SuggestedPostDecline')}
|
||||||
|
confirmLabel={lang('SuggestedPostDecline')}
|
||||||
|
confirmHandler={handleDeclineConfirm}
|
||||||
|
confirmIsDestructive
|
||||||
|
>
|
||||||
|
<div className="decline-dialog-question">
|
||||||
|
{renderText(lang('DeclinePostDialogQuestion', {
|
||||||
|
sender: sender ? getPeerFullTitle(oldLang, sender) : '',
|
||||||
|
}, { withNodes: true, withMarkdown: true }))}
|
||||||
|
</div>
|
||||||
|
<InputText
|
||||||
|
placeholder={lang('DeclineReasonPlaceholder')}
|
||||||
|
value={declineReason}
|
||||||
|
onChange={handleDeclineReasonChange}
|
||||||
|
maxLength={MAX_REASON_LENGTH}
|
||||||
|
/>
|
||||||
|
</ConfirmDialog>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -1871,6 +1980,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const lastPlaybackTimestamp = selectMessageLastPlaybackTimestamp(global, chatId, message.id);
|
const lastPlaybackTimestamp = selectMessageLastPlaybackTimestamp(global, chatId, message.id);
|
||||||
const isAccountFrozen = selectIsCurrentUserFrozen(global);
|
const isAccountFrozen = selectIsCurrentUserFrozen(global);
|
||||||
|
|
||||||
|
const minFutureTime = global.appConfig?.starsSuggestedPostFutureMin || STARS_SUGGESTED_POST_FUTURE_MIN;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
theme: selectTheme(global),
|
theme: selectTheme(global),
|
||||||
forceSenderName,
|
forceSenderName,
|
||||||
@ -1957,6 +2068,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
tags: global.savedReactionTags?.byKey,
|
tags: global.savedReactionTags?.byKey,
|
||||||
canTranscribeVoice,
|
canTranscribeVoice,
|
||||||
viaBusinessBot,
|
viaBusinessBot,
|
||||||
|
minFutureTime,
|
||||||
effect,
|
effect,
|
||||||
poll,
|
poll,
|
||||||
maxTimestamp,
|
maxTimestamp,
|
||||||
|
|||||||
@ -20,6 +20,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.decline-dialog-question {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.message-content {
|
.message-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
max-width: var(--max-width);
|
max-width: var(--max-width);
|
||||||
|
|||||||
148
src/components/middle/message/actions/SuggestedPostApproval.tsx
Normal file
148
src/components/middle/message/actions/SuggestedPostApproval.tsx
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import { memo } from '../../../../lib/teact/teact';
|
||||||
|
import { withGlobal } from '../../../../global';
|
||||||
|
|
||||||
|
import type { ApiMessage, ApiPeer } from '../../../../api/types';
|
||||||
|
import type { ApiMessageActionSuggestedPostApproval } from '../../../../api/types/messageActions';
|
||||||
|
|
||||||
|
import { STARS_SUGGESTED_POST_AGE_MIN } from '../../../../config';
|
||||||
|
import { getPeerFullTitle } from '../../../../global/helpers/peers';
|
||||||
|
import { getMessageReplyInfo } from '../../../../global/helpers/replies';
|
||||||
|
import { selectIsMonoforumAdmin, selectMonoforumChannel,
|
||||||
|
selectReplyMessage,
|
||||||
|
selectSender } from '../../../../global/selectors';
|
||||||
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
|
import { formatScheduledDateTime, formatShortDuration } from '../../../../util/dates/dateFormat';
|
||||||
|
import { formatStarsAsText } from '../../../../util/localization/format';
|
||||||
|
import { getServerTime } from '../../../../util/serverTime';
|
||||||
|
import renderText from '../../../common/helpers/renderText';
|
||||||
|
import { renderPeerLink, translateWithYou } from '../helpers/messageActions';
|
||||||
|
|
||||||
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useOldLang from '../../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import styles from '../ActionMessage.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
message: ApiMessage;
|
||||||
|
action: ApiMessageActionSuggestedPostApproval;
|
||||||
|
onClick?: NoneToVoidFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
sender?: ApiPeer;
|
||||||
|
chat?: ApiPeer;
|
||||||
|
originalSender?: ApiPeer;
|
||||||
|
ageMinSeconds: number;
|
||||||
|
isAdmin: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SuggestedPostApproval = ({
|
||||||
|
message,
|
||||||
|
action,
|
||||||
|
sender,
|
||||||
|
chat,
|
||||||
|
originalSender,
|
||||||
|
ageMinSeconds,
|
||||||
|
isAdmin,
|
||||||
|
onClick,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
|
const lang = useLang();
|
||||||
|
const oldLang = useOldLang();
|
||||||
|
const { scheduleDate, amount } = action;
|
||||||
|
|
||||||
|
const chatTitle = chat && getPeerFullTitle(lang, chat);
|
||||||
|
const renderChatLink = () => renderPeerLink(chat?.id, chatTitle || lang('ActionFallbackChat'));
|
||||||
|
|
||||||
|
const originalSenderTitle = originalSender && getPeerFullTitle(lang, originalSender);
|
||||||
|
const originalSenderLink = renderPeerLink(originalSender?.id, originalSenderTitle || lang('ActionFallbackUser'));
|
||||||
|
|
||||||
|
const publishDate = scheduleDate
|
||||||
|
? formatScheduledDateTime(scheduleDate, lang, oldLang)
|
||||||
|
: lang('TitleAnytime');
|
||||||
|
|
||||||
|
const isPostPublished = scheduleDate ? scheduleDate <= getServerTime() : false;
|
||||||
|
|
||||||
|
const starsText = amount?.amount ? formatStarsAsText(lang, amount.amount) : undefined;
|
||||||
|
|
||||||
|
const duration = formatShortDuration(lang, ageMinSeconds, true);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={buildClassName(styles.contentBox, styles.suggestedPostContentBox)}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<div className={styles.suggestedPostApprovalTitle}>
|
||||||
|
{renderText(lang('SuggestedPostAgreementReached'))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.suggestedPostApprovalSection}>
|
||||||
|
{translateWithYou(
|
||||||
|
lang,
|
||||||
|
isPostPublished ? 'SuggestedPostPublished' : 'SuggestedPostPublishSchedule',
|
||||||
|
!isAdmin,
|
||||||
|
{ peer: renderChatLink(), date: publishDate },
|
||||||
|
{ withMarkdown: true },
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{starsText && (
|
||||||
|
<div className={styles.suggestedPostApprovalSection}>
|
||||||
|
{translateWithYou(lang,
|
||||||
|
'SuggestedPostCharged',
|
||||||
|
!isAdmin,
|
||||||
|
{
|
||||||
|
user: originalSenderLink,
|
||||||
|
amount: starsText,
|
||||||
|
},
|
||||||
|
{ withMarkdown: true },
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isPostPublished && starsText && (
|
||||||
|
<>
|
||||||
|
<div className={styles.suggestedPostApprovalSection}>
|
||||||
|
{translateWithYou(lang, 'SuggestedPostReceiveAmount', !isAdmin, {
|
||||||
|
peer: renderChatLink(), duration, currency: lang('CurrencyStars'),
|
||||||
|
}, { withMarkdown: true })}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.suggestedPostApprovalSection}>
|
||||||
|
{translateWithYou(lang, 'SuggestedPostRefund', !isAdmin, {
|
||||||
|
peer: renderChatLink(), duration, currency: lang('CurrencyStars'),
|
||||||
|
}, { withMarkdown: true })}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global, { message }): StateProps => {
|
||||||
|
const sender = selectSender(global, message);
|
||||||
|
const chat = selectMonoforumChannel(global, message.chatId);
|
||||||
|
|
||||||
|
const replyInfo = getMessageReplyInfo(message);
|
||||||
|
let originalSender: ApiPeer | undefined;
|
||||||
|
|
||||||
|
if (replyInfo?.type === 'message' && replyInfo.replyToMsgId) {
|
||||||
|
const replyMessage = selectReplyMessage(global, message);
|
||||||
|
if (replyMessage) {
|
||||||
|
originalSender = selectSender(global, replyMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { appConfig } = global;
|
||||||
|
const ageMinSeconds = appConfig?.starsSuggestedPostAgeMin || STARS_SUGGESTED_POST_AGE_MIN;
|
||||||
|
const isAdmin = chat ? Boolean(selectIsMonoforumAdmin(global, message.chatId)) : false;
|
||||||
|
|
||||||
|
return {
|
||||||
|
sender,
|
||||||
|
chat,
|
||||||
|
originalSender,
|
||||||
|
ageMinSeconds,
|
||||||
|
isAdmin,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(SuggestedPostApproval));
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
import { memo } from '../../../../lib/teact/teact';
|
||||||
|
import { getActions, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
|
import type { ApiMessage, ApiPeer } from '../../../../api/types';
|
||||||
|
import type { ApiMessageActionSuggestedPostApproval } from '../../../../api/types/messageActions';
|
||||||
|
|
||||||
|
import { getPeerFullTitle } from '../../../../global/helpers/peers';
|
||||||
|
import { selectChatMessage, selectSender } from '../../../../global/selectors';
|
||||||
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
|
import { renderPeerLink } from '../helpers/messageActions';
|
||||||
|
|
||||||
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import Sparkles from '../../../common/Sparkles';
|
||||||
|
|
||||||
|
import styles from '../ActionMessage.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
message: ApiMessage;
|
||||||
|
action: ApiMessageActionSuggestedPostApproval;
|
||||||
|
onClick?: NoneToVoidFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
sender?: ApiPeer;
|
||||||
|
replyMessageSender?: ApiPeer;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SuggestedPostBalanceTooLow = ({
|
||||||
|
onClick,
|
||||||
|
message,
|
||||||
|
sender,
|
||||||
|
replyMessageSender,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
|
const { openStarsBalanceModal } = getActions();
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const handleGetMoreStars = useLastCallback((e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
openStarsBalanceModal({});
|
||||||
|
});
|
||||||
|
|
||||||
|
const targetPeer = replyMessageSender || sender;
|
||||||
|
const peerTitle = targetPeer && getPeerFullTitle(lang, targetPeer);
|
||||||
|
const peerLink = renderPeerLink(targetPeer?.id, peerTitle || lang('ActionFallbackUser'));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={buildClassName(styles.contentBox, styles.suggestedPostBalanceTooLowBox)}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<div className={styles.suggestedPostBalanceTooLowTitle}>
|
||||||
|
{lang('SuggestedPostBalanceTooLow', {
|
||||||
|
peer: peerLink,
|
||||||
|
currency: lang('CurrencyStars'),
|
||||||
|
}, { withNodes: true, withMarkdown: true })}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!message.isOutgoing && (
|
||||||
|
<div className={styles.actionButton} onClick={handleGetMoreStars}>
|
||||||
|
<Sparkles preset="button" />
|
||||||
|
{lang('ButtonBuyStars')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global, { message }): StateProps => {
|
||||||
|
const sender = selectSender(global, message);
|
||||||
|
|
||||||
|
const replyMessage = message.replyInfo?.type === 'message' && message.replyInfo.replyToMsgId
|
||||||
|
? selectChatMessage(global, message.chatId, message.replyInfo.replyToMsgId)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const replyMessageSender = replyMessage ? selectSender(global, replyMessage) : undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
sender,
|
||||||
|
replyMessageSender,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(SuggestedPostBalanceTooLow));
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
import { memo } from '../../../../lib/teact/teact';
|
||||||
|
import { withGlobal } from '../../../../global';
|
||||||
|
|
||||||
|
import type { ApiMessage, ApiPeer } from '../../../../api/types';
|
||||||
|
import type { ApiMessageActionSuggestedPostApproval } from '../../../../api/types/messageActions';
|
||||||
|
|
||||||
|
import { getPeerTitle } from '../../../../global/helpers/peers';
|
||||||
|
import { selectSender } from '../../../../global/selectors';
|
||||||
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
|
import { renderPeerLink, translateWithYou } from '../helpers/messageActions';
|
||||||
|
|
||||||
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
|
||||||
|
import Icon from '../../../common/icons/Icon';
|
||||||
|
|
||||||
|
import styles from '../ActionMessage.module.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
message: ApiMessage;
|
||||||
|
action: ApiMessageActionSuggestedPostApproval;
|
||||||
|
onClick?: NoneToVoidFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
sender?: ApiPeer;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SuggestedPostRejected = ({
|
||||||
|
message,
|
||||||
|
action,
|
||||||
|
sender,
|
||||||
|
onClick,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
|
const lang = useLang();
|
||||||
|
const { isOutgoing } = message;
|
||||||
|
const { rejectComment } = action;
|
||||||
|
|
||||||
|
const senderTitle = sender && getPeerTitle(lang, sender);
|
||||||
|
const senderLink = renderPeerLink(sender?.id, senderTitle || lang('ActionFallbackUser'));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={buildClassName(styles.contentBox, styles.suggestedPostRejectedContentBox)}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<div className={styles.suggestedPostRejectedTitle}>
|
||||||
|
<Icon className={styles.rejectedIcon} name="close" />
|
||||||
|
{translateWithYou(
|
||||||
|
lang,
|
||||||
|
rejectComment ? 'SuggestedPostRejectedWithReason' : 'SuggestedPostRejected',
|
||||||
|
isOutgoing,
|
||||||
|
{ peer: senderLink },
|
||||||
|
{ withMarkdown: true },
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{rejectComment && (
|
||||||
|
<div className={styles.suggestedPostRejectedComment}>
|
||||||
|
{lang('SuggestedPostRejectedComment', { comment: rejectComment })}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global, { message }): StateProps => {
|
||||||
|
const sender = selectSender(global, message);
|
||||||
|
|
||||||
|
return {
|
||||||
|
sender,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(SuggestedPostRejected));
|
||||||
@ -42,7 +42,9 @@ import StarsBalanceModal from './stars/StarsBalanceModal.async';
|
|||||||
import StarsPaymentModal from './stars/StarsPaymentModal.async';
|
import StarsPaymentModal from './stars/StarsPaymentModal.async';
|
||||||
import StarsSubscriptionModal from './stars/subscription/StarsSubscriptionModal.async';
|
import StarsSubscriptionModal from './stars/subscription/StarsSubscriptionModal.async';
|
||||||
import StarsTransactionInfoModal from './stars/transaction/StarsTransactionModal.async';
|
import StarsTransactionInfoModal from './stars/transaction/StarsTransactionModal.async';
|
||||||
|
import SuggestedPostApprovalModal from './suggestedPostApproval/SuggestedPostApprovalModal.async';
|
||||||
import SuggestedStatusModal from './suggestedStatus/SuggestedStatusModal.async';
|
import SuggestedStatusModal from './suggestedStatus/SuggestedStatusModal.async';
|
||||||
|
import SuggestMessageModal from './suggestMessage/SuggestMessageModal.async';
|
||||||
import UrlAuthModal from './urlAuth/UrlAuthModal.async';
|
import UrlAuthModal from './urlAuth/UrlAuthModal.async';
|
||||||
import WebAppModal from './webApp/WebAppModal.async';
|
import WebAppModal from './webApp/WebAppModal.async';
|
||||||
|
|
||||||
@ -63,6 +65,8 @@ type ModalKey = keyof Pick<TabState,
|
|||||||
'starsPayment' |
|
'starsPayment' |
|
||||||
'starsTransactionModal' |
|
'starsTransactionModal' |
|
||||||
'paidReactionModal' |
|
'paidReactionModal' |
|
||||||
|
'suggestMessageModal' |
|
||||||
|
'suggestedPostApprovalModal' |
|
||||||
'webApps' |
|
'webApps' |
|
||||||
'chatInviteModal' |
|
'chatInviteModal' |
|
||||||
'starsSubscriptionModal' |
|
'starsSubscriptionModal' |
|
||||||
@ -118,6 +122,8 @@ const MODALS: ModalRegistry = {
|
|||||||
starsTransactionModal: StarsTransactionInfoModal,
|
starsTransactionModal: StarsTransactionInfoModal,
|
||||||
chatInviteModal: ChatInviteModal,
|
chatInviteModal: ChatInviteModal,
|
||||||
paidReactionModal: PaidReactionModal,
|
paidReactionModal: PaidReactionModal,
|
||||||
|
suggestMessageModal: SuggestMessageModal,
|
||||||
|
suggestedPostApprovalModal: SuggestedPostApprovalModal,
|
||||||
starsSubscriptionModal: StarsSubscriptionModal,
|
starsSubscriptionModal: StarsSubscriptionModal,
|
||||||
starsGiftModal: StarsGiftModal,
|
starsGiftModal: StarsGiftModal,
|
||||||
giftModal: PremiumGiftModal,
|
giftModal: PremiumGiftModal,
|
||||||
|
|||||||
@ -11,6 +11,23 @@
|
|||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.subtitleBadge {
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
padding: 0.75rem;
|
||||||
|
padding-block: 0.125rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
backdrop-filter: blur(50px);
|
||||||
|
|
||||||
|
transition: color 150ms ease-in, background-color 0.15s !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.radialPattern {
|
.radialPattern {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
|
import type { TeactNode } from '../../../lib/teact/teact';
|
||||||
import { memo, useMemo } from '../../../lib/teact/teact';
|
import { memo, useMemo } from '../../../lib/teact/teact';
|
||||||
|
import { getActions } from '../../../global';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
ApiPeer,
|
||||||
ApiStarGiftAttributeBackdrop, ApiStarGiftAttributeModel, ApiStarGiftAttributePattern,
|
ApiStarGiftAttributeBackdrop, ApiStarGiftAttributeModel, ApiStarGiftAttributePattern,
|
||||||
ApiStarsAmount,
|
ApiStarsAmount } from '../../../api/types';
|
||||||
} from '../../../api/types';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
formatStarsTransactionAmount,
|
formatStarsTransactionAmount,
|
||||||
@ -26,7 +28,8 @@ type OwnProps = {
|
|||||||
backdropAttribute: ApiStarGiftAttributeBackdrop;
|
backdropAttribute: ApiStarGiftAttributeBackdrop;
|
||||||
patternAttribute: ApiStarGiftAttributePattern;
|
patternAttribute: ApiStarGiftAttributePattern;
|
||||||
title?: string;
|
title?: string;
|
||||||
subtitle?: string;
|
subtitle?: TeactNode;
|
||||||
|
subtitlePeer?: ApiPeer;
|
||||||
className?: string;
|
className?: string;
|
||||||
resellPrice?: ApiStarsAmount;
|
resellPrice?: ApiStarsAmount;
|
||||||
};
|
};
|
||||||
@ -39,9 +42,14 @@ const UniqueGiftHeader = ({
|
|||||||
patternAttribute,
|
patternAttribute,
|
||||||
title,
|
title,
|
||||||
subtitle,
|
subtitle,
|
||||||
|
subtitlePeer,
|
||||||
className,
|
className,
|
||||||
resellPrice,
|
resellPrice,
|
||||||
}: OwnProps) => {
|
}: OwnProps) => {
|
||||||
|
const {
|
||||||
|
openChat,
|
||||||
|
} = getActions();
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const activeKey = useTransitionActiveKey([modelAttribute, backdropAttribute, patternAttribute]);
|
const activeKey = useTransitionActiveKey([modelAttribute, backdropAttribute, patternAttribute]);
|
||||||
const subtitleColor = backdropAttribute?.textColor;
|
const subtitleColor = backdropAttribute?.textColor;
|
||||||
@ -77,10 +85,18 @@ const UniqueGiftHeader = ({
|
|||||||
/>
|
/>
|
||||||
</Transition>
|
</Transition>
|
||||||
{title && <h1 className={styles.title}>{title}</h1>}
|
{title && <h1 className={styles.title}>{title}</h1>}
|
||||||
{subtitle && (
|
{Boolean(subtitle) && (
|
||||||
<p className={styles.subtitle} style={buildStyle(subtitleColor && `color: ${subtitleColor}`)}>
|
<div
|
||||||
|
className={buildClassName(styles.subtitle, subtitlePeer && styles.subtitleBadge)}
|
||||||
|
style={buildStyle(subtitleColor && `color: ${subtitleColor}`)}
|
||||||
|
onClick={() => {
|
||||||
|
if (subtitlePeer) {
|
||||||
|
openChat({ id: subtitlePeer.id });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
{subtitle}
|
{subtitle}
|
||||||
</p>
|
</div>
|
||||||
)}
|
)}
|
||||||
{resellPrice && (
|
{resellPrice && (
|
||||||
<p className={styles.amount}>
|
<p className={styles.amount}>
|
||||||
|
|||||||
@ -10,7 +10,8 @@ import type {
|
|||||||
import type { TabState } from '../../../../global/types';
|
import type { TabState } from '../../../../global/types';
|
||||||
|
|
||||||
import { getHasAdminRight } from '../../../../global/helpers';
|
import { getHasAdminRight } from '../../../../global/helpers';
|
||||||
import { getPeerTitle, isApiPeerChat } from '../../../../global/helpers/peers';
|
import { getPeerTitle, isApiPeerChat, isApiPeerUser } from '../../../../global/helpers/peers';
|
||||||
|
import { getMainUsername } from '../../../../global/helpers/users';
|
||||||
import { selectPeer, selectUser } from '../../../../global/selectors';
|
import { selectPeer, selectUser } from '../../../../global/selectors';
|
||||||
import buildClassName from '../../../../util/buildClassName';
|
import buildClassName from '../../../../util/buildClassName';
|
||||||
import { copyTextToClipboard } from '../../../../util/clipboard';
|
import { copyTextToClipboard } from '../../../../util/clipboard';
|
||||||
@ -51,6 +52,7 @@ export type OwnProps = {
|
|||||||
type StateProps = {
|
type StateProps = {
|
||||||
fromPeer?: ApiPeer;
|
fromPeer?: ApiPeer;
|
||||||
targetPeer?: ApiPeer;
|
targetPeer?: ApiPeer;
|
||||||
|
releasedByPeer?: ApiPeer;
|
||||||
currentUserId?: string;
|
currentUserId?: string;
|
||||||
starGiftMaxConvertPeriod?: number;
|
starGiftMaxConvertPeriod?: number;
|
||||||
hasAdminRights?: boolean;
|
hasAdminRights?: boolean;
|
||||||
@ -67,6 +69,7 @@ const GiftInfoModal = ({
|
|||||||
modal,
|
modal,
|
||||||
fromPeer,
|
fromPeer,
|
||||||
targetPeer,
|
targetPeer,
|
||||||
|
releasedByPeer,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
starGiftMaxConvertPeriod,
|
starGiftMaxConvertPeriod,
|
||||||
hasAdminRights,
|
hasAdminRights,
|
||||||
@ -117,6 +120,26 @@ const GiftInfoModal = ({
|
|||||||
const isGiftUnique = gift && gift.type === 'starGiftUnique';
|
const isGiftUnique = gift && gift.type === 'starGiftUnique';
|
||||||
const uniqueGift = isGiftUnique ? gift : undefined;
|
const uniqueGift = isGiftUnique ? gift : undefined;
|
||||||
|
|
||||||
|
const giftSubtitle = useMemo(() => {
|
||||||
|
if (!gift || gift.type !== 'starGiftUnique') return undefined;
|
||||||
|
|
||||||
|
if (releasedByPeer) {
|
||||||
|
const releasedByUsername = `@${getMainUsername(releasedByPeer)}`;
|
||||||
|
const ownerTitle = releasedByUsername || getPeerTitle(lang, releasedByPeer);
|
||||||
|
const fallbackText = isApiPeerUser(releasedByPeer)
|
||||||
|
? lang('ActionFallbackUser')
|
||||||
|
: lang('ActionFallbackChannel');
|
||||||
|
|
||||||
|
return lang('GiftInfoCollectibleBy', {
|
||||||
|
number: gift.number, owner: ownerTitle || fallbackText }, {
|
||||||
|
withNodes: true,
|
||||||
|
withMarkdown: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return lang('GiftInfoCollectible', { number: gift.number });
|
||||||
|
}, [gift, releasedByPeer, lang]);
|
||||||
|
|
||||||
const canFocusUpgrade = Boolean(savedGift?.upgradeMsgId);
|
const canFocusUpgrade = Boolean(savedGift?.upgradeMsgId);
|
||||||
const canManage = !canFocusUpgrade && savedGift?.inputGift && (
|
const canManage = !canFocusUpgrade && savedGift?.inputGift && (
|
||||||
isTargetChat ? hasAdminRights : renderingTargetPeer?.id === currentUserId
|
isTargetChat ? hasAdminRights : renderingTargetPeer?.id === currentUserId
|
||||||
@ -406,7 +429,8 @@ const GiftInfoModal = ({
|
|||||||
patternAttribute={giftAttributes!.pattern!}
|
patternAttribute={giftAttributes!.pattern!}
|
||||||
modelAttribute={giftAttributes!.model!}
|
modelAttribute={giftAttributes!.model!}
|
||||||
title={gift.title}
|
title={gift.title}
|
||||||
subtitle={lang('GiftInfoCollectible', { number: gift.number })}
|
subtitle={giftSubtitle}
|
||||||
|
subtitlePeer={releasedByPeer}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -512,6 +536,7 @@ const GiftInfoModal = ({
|
|||||||
|
|
||||||
if (isGiftUnique) {
|
if (isGiftUnique) {
|
||||||
const { ownerName, ownerAddress, ownerId } = gift;
|
const { ownerName, ownerAddress, ownerId } = gift;
|
||||||
|
const ownerPeer = ownerId ? selectPeer(getGlobal(), ownerId) : undefined;
|
||||||
const {
|
const {
|
||||||
model, backdrop, pattern, originalDetails,
|
model, backdrop, pattern, originalDetails,
|
||||||
} = giftAttributes || {};
|
} = giftAttributes || {};
|
||||||
@ -533,7 +558,7 @@ const GiftInfoModal = ({
|
|||||||
<Icon className={styles.copyIcon} name="copy" />
|
<Icon className={styles.copyIcon} name="copy" />
|
||||||
</span>,
|
</span>,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else if (ownerPeer || ownerName) {
|
||||||
tableData.push([
|
tableData.push([
|
||||||
lang('GiftInfoOwner'),
|
lang('GiftInfoOwner'),
|
||||||
ownerId ? { chatId: ownerId, withEmojiStatus: true } : ownerName || '',
|
ownerId ? { chatId: ownerId, withEmojiStatus: true } : ownerName || '',
|
||||||
@ -702,7 +727,8 @@ const GiftInfoModal = ({
|
|||||||
gift, giftAttributes, renderFooterButton, isTargetChat,
|
gift, giftAttributes, renderFooterButton, isTargetChat,
|
||||||
SettingsMenuButton, isGiftUnique, renderingModal,
|
SettingsMenuButton, isGiftUnique, renderingModal,
|
||||||
collectibleEmojiStatuses, currentUserEmojiStatus, saleDateInfo,
|
collectibleEmojiStatuses, currentUserEmojiStatus, saleDateInfo,
|
||||||
canBuyGift, giftOwnerTitle, isOpen, resellPriceInStars,
|
canBuyGift, giftOwnerTitle, isOpen, resellPriceInStars, giftSubtitle,
|
||||||
|
releasedByPeer,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -812,9 +838,14 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const currentUserEmojiStatus = currentUser?.emojiStatus;
|
const currentUserEmojiStatus = currentUser?.emojiStatus;
|
||||||
const collectibleEmojiStatuses = global.collectibleEmojiStatuses?.statuses;
|
const collectibleEmojiStatuses = global.collectibleEmojiStatuses?.statuses;
|
||||||
|
|
||||||
|
const gift = isSavedGift ? typeGift.gift : typeGift;
|
||||||
|
const releasedByPeerId = gift?.type === 'starGiftUnique' && gift.releasedByPeerId;
|
||||||
|
const releasedByPeer = releasedByPeerId ? selectPeer(global, releasedByPeerId) : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fromPeer,
|
fromPeer,
|
||||||
targetPeer,
|
targetPeer,
|
||||||
|
releasedByPeer,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
starGiftMaxConvertPeriod: global.appConfig?.starGiftMaxConvertPeriod,
|
starGiftMaxConvertPeriod: global.appConfig?.starGiftMaxConvertPeriod,
|
||||||
tonExplorerUrl: global.appConfig?.tonExplorerUrl,
|
tonExplorerUrl: global.appConfig?.tonExplorerUrl,
|
||||||
|
|||||||
@ -40,6 +40,7 @@ const ChatRefundModal = ({ modal, user }: OwnProps & StateProps) => {
|
|||||||
const handleConfirmRemoveFee = useLastCallback(() => {
|
const handleConfirmRemoveFee = useLastCallback(() => {
|
||||||
closeChatRefundModal();
|
closeChatRefundModal();
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
toggleNoPaidMessagesException ({ userId, shouldRefundCharged: shouldRefundStars });
|
toggleNoPaidMessagesException ({ userId, shouldRefundCharged: shouldRefundStars });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,18 @@
|
|||||||
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './SuggestMessageModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
import Loading from '../../ui/Loading';
|
||||||
|
|
||||||
|
const SuggestMessageModalAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const SuggestMessageModal = useModuleLoader(Bundles.Extra, 'SuggestMessageModal', !modal);
|
||||||
|
|
||||||
|
return SuggestMessageModal ? <SuggestMessageModal {...props} /> : <Loading />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SuggestMessageModalAsync;
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-height: min(92vh, 32rem) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modalHeader {
|
||||||
|
padding-top: 0.25rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section,
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
margin-block: 0.5rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-left: 1rem;
|
||||||
|
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeInputIcon {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 1rem;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
|
||||||
|
transition: color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.offerButton {
|
||||||
|
font-weight: var(--font-weight-medium);
|
||||||
|
}
|
||||||
256
src/components/modals/suggestMessage/SuggestMessageModal.tsx
Normal file
256
src/components/modals/suggestMessage/SuggestMessageModal.tsx
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
import type React from '../../../lib/teact/teact';
|
||||||
|
import {
|
||||||
|
memo, useEffect,
|
||||||
|
useState } from '../../../lib/teact/teact';
|
||||||
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
|
import type { ApiDraft, ApiStarsAmount } from '../../../api/types';
|
||||||
|
import type { ApiPeer } from '../../../api/types';
|
||||||
|
import type { TabState } from '../../../global/types';
|
||||||
|
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||||
|
|
||||||
|
import {
|
||||||
|
STARS_SUGGESTED_POST_AGE_MIN,
|
||||||
|
STARS_SUGGESTED_POST_AMOUNT_MAX,
|
||||||
|
STARS_SUGGESTED_POST_AMOUNT_MIN,
|
||||||
|
STARS_SUGGESTED_POST_FUTURE_MAX,
|
||||||
|
STARS_SUGGESTED_POST_FUTURE_MIN } from '../../../config';
|
||||||
|
import { selectPeer } from '../../../global/selectors';
|
||||||
|
import { selectDraft } from '../../../global/selectors/messages';
|
||||||
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
import { formatScheduledDateTime, formatShortDuration } from '../../../util/dates/dateFormat';
|
||||||
|
import { formatStarsAsIcon, formatStarsAsText } from '../../../util/localization/format';
|
||||||
|
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import CalendarModal from '../../common/CalendarModal';
|
||||||
|
import Icon from '../../common/icons/Icon';
|
||||||
|
import Button from '../../ui/Button';
|
||||||
|
import InputText from '../../ui/InputText';
|
||||||
|
import Modal from '../../ui/Modal';
|
||||||
|
|
||||||
|
import styles from './SuggestMessageModal.module.scss';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['suggestMessageModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
starBalance?: ApiStarsAmount;
|
||||||
|
peer?: ApiPeer;
|
||||||
|
currentDraft?: ApiDraft;
|
||||||
|
maxAmount: number;
|
||||||
|
minAmount: number;
|
||||||
|
ageMinSeconds: number;
|
||||||
|
futureMin: number;
|
||||||
|
futureMax: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SuggestMessageModal = ({
|
||||||
|
modal,
|
||||||
|
starBalance,
|
||||||
|
peer,
|
||||||
|
currentDraft,
|
||||||
|
maxAmount,
|
||||||
|
minAmount,
|
||||||
|
ageMinSeconds,
|
||||||
|
futureMin,
|
||||||
|
futureMax,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
|
const { closeSuggestMessageModal, updateDraftSuggestedPostInfo, openStarsBalanceModal } = getActions();
|
||||||
|
const [isCalendarOpened, openCalendar, closeCalendar] = useFlag();
|
||||||
|
|
||||||
|
const currentSuggestedPostInfo = currentDraft?.suggestedPostInfo;
|
||||||
|
const currentReplyInfo = currentDraft?.replyInfo;
|
||||||
|
const isInSuggestChangesMode = Boolean(currentReplyInfo);
|
||||||
|
|
||||||
|
const [starsAmount, setStarsAmount] = useState<number | undefined>(
|
||||||
|
currentSuggestedPostInfo?.price?.amount || undefined,
|
||||||
|
);
|
||||||
|
const [scheduleDate, setScheduleDate] = useState<number | undefined>(
|
||||||
|
currentSuggestedPostInfo?.scheduleDate
|
||||||
|
? currentSuggestedPostInfo.scheduleDate * 1000
|
||||||
|
: undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setStarsAmount(currentSuggestedPostInfo?.price?.amount || undefined);
|
||||||
|
setScheduleDate(currentSuggestedPostInfo?.scheduleDate
|
||||||
|
? currentSuggestedPostInfo.scheduleDate * 1000
|
||||||
|
: undefined);
|
||||||
|
}, [currentSuggestedPostInfo]);
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
const oldLang = useOldLang();
|
||||||
|
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const minAt = (now + futureMin) * 1000;
|
||||||
|
const maxAt = (now + futureMax) * 1000;
|
||||||
|
const defaultSelectedTime = (now + futureMin * 2) * 1000;
|
||||||
|
|
||||||
|
const handleAmountChange = useLastCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
const number = parseFloat(value);
|
||||||
|
|
||||||
|
const result = value === '' || Number.isNaN(number) ? undefined
|
||||||
|
: Math.min(Math.max(number, 0), maxAmount);
|
||||||
|
|
||||||
|
setStarsAmount(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleExpireDateChange = useLastCallback((date: Date) => {
|
||||||
|
setScheduleDate(date.getTime());
|
||||||
|
closeCalendar();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleAnytimeClick = useLastCallback(() => {
|
||||||
|
setScheduleDate(undefined);
|
||||||
|
closeCalendar();
|
||||||
|
});
|
||||||
|
|
||||||
|
const isDisabled = Boolean(starsAmount) && starsAmount < minAmount;
|
||||||
|
|
||||||
|
const handleOffer = useLastCallback(() => {
|
||||||
|
const neededAmount = starsAmount || 0;
|
||||||
|
|
||||||
|
if (isDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentBalance = starBalance?.amount || 0;
|
||||||
|
|
||||||
|
if (neededAmount > currentBalance) {
|
||||||
|
openStarsBalanceModal({
|
||||||
|
topup: {
|
||||||
|
balanceNeeded: neededAmount,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDraftSuggestedPostInfo({
|
||||||
|
price: { amount: neededAmount, nanos: 0 },
|
||||||
|
scheduleDate: scheduleDate ? scheduleDate / 1000 : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
closeSuggestMessageModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
headerClassName={styles.modalHeader}
|
||||||
|
isOpen={Boolean(modal)}
|
||||||
|
onClose={closeSuggestMessageModal}
|
||||||
|
isSlim
|
||||||
|
isLowStackPriority
|
||||||
|
hasCloseButton
|
||||||
|
contentClassName={styles.content}
|
||||||
|
title={isInSuggestChangesMode ? lang('TitleSuggestedChanges') : lang('TitleSuggestMessage')}
|
||||||
|
>
|
||||||
|
<div className={styles.form}>
|
||||||
|
<div className={styles.section}>
|
||||||
|
<InputText
|
||||||
|
label={lang('InputPlaceholderPrice')}
|
||||||
|
className={buildClassName(styles.input)}
|
||||||
|
value={starsAmount?.toString()}
|
||||||
|
onChange={handleAmountChange}
|
||||||
|
inputMode="numeric"
|
||||||
|
tabIndex={0}
|
||||||
|
teactExperimentControlled
|
||||||
|
/>
|
||||||
|
<div className={styles.description}>
|
||||||
|
{starsAmount !== undefined && starsAmount > 0 && starsAmount < minAmount
|
||||||
|
? lang('DescriptionSuggestedPostMinimumOffer', {
|
||||||
|
amount: formatStarsAsText(lang, minAmount) },
|
||||||
|
{ withNodes: true, withMarkdown: true })
|
||||||
|
: lang('SuggestMessagePriceDescription', {
|
||||||
|
currency: lang('CurrencyStars'),
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<div className={buildClassName('input-group', 'touched')}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className={buildClassName('form-control', isCalendarOpened && 'focus')}
|
||||||
|
value={scheduleDate ? formatScheduledDateTime(scheduleDate / 1000, lang, oldLang) : lang('TitleAnytime')}
|
||||||
|
autoComplete="off"
|
||||||
|
onClick={openCalendar}
|
||||||
|
onFocus={openCalendar}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
<label>{lang('InputTitleSuggestMessageTime')}</label>
|
||||||
|
<Icon name="down" className={styles.timeInputIcon} />
|
||||||
|
</div>
|
||||||
|
<div className={styles.description}>
|
||||||
|
{lang('SuggestMessageTimeDescription', {
|
||||||
|
hint: lang('SuggestMessageDateTimeHint'),
|
||||||
|
duration: formatShortDuration(lang, ageMinSeconds, true),
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CalendarModal
|
||||||
|
isOpen={isCalendarOpened}
|
||||||
|
isFutureMode
|
||||||
|
withTimePicker
|
||||||
|
minAt={minAt}
|
||||||
|
maxAt={maxAt}
|
||||||
|
onClose={closeCalendar}
|
||||||
|
onSubmit={handleExpireDateChange}
|
||||||
|
selectedAt={scheduleDate || defaultSelectedTime}
|
||||||
|
submitButtonLabel={lang('Save')}
|
||||||
|
secondButtonLabel={lang('TitleAnytime')}
|
||||||
|
onSecondButtonClick={handleAnytimeClick}
|
||||||
|
description={lang('SuggestMessageDateTimeHint')}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className={styles.offerButton}
|
||||||
|
onClick={handleOffer}
|
||||||
|
size="smaller"
|
||||||
|
disabled={isDisabled}
|
||||||
|
>
|
||||||
|
{isInSuggestChangesMode ? lang('ButtonUpdateTerms')
|
||||||
|
: starsAmount ? lang('ButtonOfferAmount', {
|
||||||
|
amount: formatStarsAsIcon(lang, starsAmount, { asFont: true }),
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
withMarkdown: true,
|
||||||
|
}) : lang('ButtonOfferFree')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global, { modal }): StateProps => {
|
||||||
|
const starBalance = global.stars?.balance;
|
||||||
|
const peer = modal ? selectPeer(global, modal.chatId) : undefined;
|
||||||
|
const currentDraft = modal ? selectDraft(global, modal.chatId, MAIN_THREAD_ID) : undefined;
|
||||||
|
|
||||||
|
const { appConfig } = global;
|
||||||
|
const maxAmount = appConfig?.starsSuggestedPostAmountMax || STARS_SUGGESTED_POST_AMOUNT_MAX;
|
||||||
|
const minAmount = appConfig?.starsSuggestedPostAmountMin || STARS_SUGGESTED_POST_AMOUNT_MIN;
|
||||||
|
const ageMinSeconds = appConfig?.starsSuggestedPostAgeMin || STARS_SUGGESTED_POST_AGE_MIN;
|
||||||
|
const futureMin = appConfig?.starsSuggestedPostFutureMin || STARS_SUGGESTED_POST_FUTURE_MIN;
|
||||||
|
const futureMax = appConfig?.starsSuggestedPostFutureMax || STARS_SUGGESTED_POST_FUTURE_MAX;
|
||||||
|
|
||||||
|
return {
|
||||||
|
peer,
|
||||||
|
starBalance,
|
||||||
|
currentDraft,
|
||||||
|
maxAmount,
|
||||||
|
minAmount,
|
||||||
|
ageMinSeconds,
|
||||||
|
futureMin,
|
||||||
|
futureMax,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(SuggestMessageModal));
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import type { FC } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { OwnProps } from './SuggestedPostApprovalModal';
|
||||||
|
|
||||||
|
import { Bundles } from '../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
import Loading from '../../ui/Loading';
|
||||||
|
|
||||||
|
const SuggestedPostApprovalModalAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { modal } = props;
|
||||||
|
const SuggestedPostApprovalModal = useModuleLoader(Bundles.Extra, 'SuggestedPostApprovalModal', !modal);
|
||||||
|
|
||||||
|
return SuggestedPostApprovalModal ? <SuggestedPostApprovalModal {...props} /> : <Loading />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SuggestedPostApprovalModalAsync;
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
.details {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
@ -0,0 +1,234 @@
|
|||||||
|
import { memo, useState } from '../../../lib/teact/teact';
|
||||||
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
|
import type { ApiMessage, ApiPeer } from '../../../api/types';
|
||||||
|
import type { TabState } from '../../../global/types';
|
||||||
|
|
||||||
|
import { STARS_SUGGESTED_POST_AGE_MIN,
|
||||||
|
STARS_SUGGESTED_POST_COMMISSION_PERMILLE,
|
||||||
|
STARS_SUGGESTED_POST_FUTURE_MAX,
|
||||||
|
STARS_SUGGESTED_POST_FUTURE_MIN,
|
||||||
|
} from '../../../config';
|
||||||
|
import { getPeerFullTitle } from '../../../global/helpers/peers';
|
||||||
|
import { selectChatMessage, selectIsMonoforumAdmin, selectSender } from '../../../global/selectors';
|
||||||
|
import { formatScheduledDateTime, formatShortDuration } from '../../../util/dates/dateFormat';
|
||||||
|
import { formatStarsAsText } from '../../../util/localization/format';
|
||||||
|
import renderText from '../../common/helpers/renderText';
|
||||||
|
|
||||||
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../../hooks/useLastCallback';
|
||||||
|
import useOldLang from '../../../hooks/useOldLang';
|
||||||
|
|
||||||
|
import CalendarModal from '../../common/CalendarModal';
|
||||||
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
|
|
||||||
|
import styles from './SuggestedPostApprovalModal.module.scss';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
modal: TabState['suggestedPostApprovalModal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
commissionPermille: number;
|
||||||
|
minAge: number;
|
||||||
|
futureMin: number;
|
||||||
|
futureMax: number;
|
||||||
|
message?: ApiMessage;
|
||||||
|
sender?: ApiPeer;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
scheduleDate?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SuggestedPostApprovalModal = ({
|
||||||
|
modal,
|
||||||
|
message,
|
||||||
|
sender,
|
||||||
|
isAdmin,
|
||||||
|
commissionPermille,
|
||||||
|
minAge,
|
||||||
|
futureMin,
|
||||||
|
futureMax,
|
||||||
|
scheduleDate,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
|
const {
|
||||||
|
closeSuggestedPostApprovalModal,
|
||||||
|
approveSuggestedPost,
|
||||||
|
} = getActions();
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
const oldLang = useOldLang();
|
||||||
|
const [isCalendarOpened, openCalendar, closeCalendar] = useFlag();
|
||||||
|
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const minAt = (now + futureMin) * 1000;
|
||||||
|
const maxAt = (now + futureMax) * 1000;
|
||||||
|
const defaultSelectedTime = now + futureMin * 2;
|
||||||
|
|
||||||
|
const [selectedScheduleDate, setSelectedScheduleDate] = useState<number>(defaultSelectedTime);
|
||||||
|
|
||||||
|
const handleClose = useLastCallback(() => {
|
||||||
|
closeSuggestedPostApprovalModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleApprove = useLastCallback((date?: number) => {
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
approveSuggestedPost({
|
||||||
|
chatId: modal.chatId,
|
||||||
|
messageId: modal.messageId,
|
||||||
|
scheduleDate: date,
|
||||||
|
});
|
||||||
|
|
||||||
|
closeSuggestedPostApprovalModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleCalendarDateChange = useLastCallback((date: Date) => {
|
||||||
|
const time = Math.floor(date.getTime() / 1000);
|
||||||
|
setSelectedScheduleDate(time);
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleCalendarSubmit = useLastCallback((date: Date) => {
|
||||||
|
const time = Math.floor(date.getTime() / 1000);
|
||||||
|
closeCalendar();
|
||||||
|
handleApprove(time);
|
||||||
|
});
|
||||||
|
|
||||||
|
const handlePublishNow = useLastCallback(() => {
|
||||||
|
closeCalendar();
|
||||||
|
handleApprove();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleNext = useLastCallback(() => {
|
||||||
|
if (scheduleDate) {
|
||||||
|
handleApprove(scheduleDate);
|
||||||
|
} else {
|
||||||
|
openCalendar();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!modal || !message) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const senderName = sender ? getPeerFullTitle(oldLang, sender) : '';
|
||||||
|
|
||||||
|
const renderContent = () => {
|
||||||
|
const amount = message?.suggestedPostInfo?.price?.amount;
|
||||||
|
const question = lang(
|
||||||
|
'SuggestedPostConfirmMessage',
|
||||||
|
{ peer: senderName },
|
||||||
|
{ withNodes: true, withMarkdown: true });
|
||||||
|
|
||||||
|
const questionText = renderText(question);
|
||||||
|
if (!amount) {
|
||||||
|
return questionText;
|
||||||
|
}
|
||||||
|
|
||||||
|
const commission = (commissionPermille / 10);
|
||||||
|
const amountWithCommission = amount / 100 * commission;
|
||||||
|
|
||||||
|
const starsText = formatStarsAsText(lang, amountWithCommission);
|
||||||
|
|
||||||
|
const ageMinSeconds = minAge;
|
||||||
|
const duration = formatShortDuration(lang, ageMinSeconds, true);
|
||||||
|
|
||||||
|
if (scheduleDate) {
|
||||||
|
const time = formatScheduledDateTime(scheduleDate, lang, oldLang);
|
||||||
|
|
||||||
|
const key
|
||||||
|
= isAdmin ? 'SuggestedPostConfirmDetailsWithTimeAdmin' : 'SuggestedPostConfirmDetailsWithTimeUser';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
{questionText}
|
||||||
|
</div>
|
||||||
|
<div className={styles.details}>
|
||||||
|
{renderText(lang(key, {
|
||||||
|
amount: starsText,
|
||||||
|
commission,
|
||||||
|
duration,
|
||||||
|
time,
|
||||||
|
}, { withNodes: true, withMarkdown: true }))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = isAdmin ? 'SuggestedPostConfirmDetailsAdmin' : 'SuggestedPostConfirmDetailsUser';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
{questionText}
|
||||||
|
</div>
|
||||||
|
<div className={styles.details}>
|
||||||
|
{renderText(lang(key, {
|
||||||
|
amount: starsText,
|
||||||
|
commission,
|
||||||
|
duration,
|
||||||
|
}, { withNodes: true, withMarkdown: true }))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ConfirmDialog
|
||||||
|
isOpen={Boolean(modal) && !isCalendarOpened}
|
||||||
|
onClose={handleClose}
|
||||||
|
title={lang('SuggestedPostConfirmTitle')}
|
||||||
|
confirmHandler={handleNext}
|
||||||
|
confirmLabel={scheduleDate ? lang('ButtonPublish') : lang('Next')}
|
||||||
|
>
|
||||||
|
{renderContent()}
|
||||||
|
</ConfirmDialog>
|
||||||
|
|
||||||
|
<CalendarModal
|
||||||
|
isOpen={isCalendarOpened}
|
||||||
|
isFutureMode
|
||||||
|
withTimePicker
|
||||||
|
minAt={minAt}
|
||||||
|
maxAt={maxAt}
|
||||||
|
onClose={closeCalendar}
|
||||||
|
onSubmit={handleCalendarSubmit}
|
||||||
|
onDateChange={handleCalendarDateChange}
|
||||||
|
selectedAt={selectedScheduleDate * 1000}
|
||||||
|
submitButtonLabel={lang('ButtonPublishAtTime', {
|
||||||
|
time: formatScheduledDateTime(selectedScheduleDate, lang, oldLang),
|
||||||
|
})}
|
||||||
|
secondButtonLabel={lang('PublishNow')}
|
||||||
|
onSecondButtonClick={handlePublishNow}
|
||||||
|
description={lang('SuggestMessageDateTimeHint')}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal<OwnProps>(
|
||||||
|
(global, { modal }): StateProps => {
|
||||||
|
const message = modal && selectChatMessage(global, modal.chatId, modal.messageId);
|
||||||
|
const sender = message ? selectSender(global, message) : undefined;
|
||||||
|
const isAdmin = modal && selectIsMonoforumAdmin(global, modal.chatId);
|
||||||
|
const { appConfig } = global;
|
||||||
|
const commissionPermille = appConfig?.starsSuggestedPostCommissionPermille
|
||||||
|
|| STARS_SUGGESTED_POST_COMMISSION_PERMILLE;
|
||||||
|
const minAge = appConfig?.starsSuggestedPostAgeMin || STARS_SUGGESTED_POST_AGE_MIN;
|
||||||
|
const futureMin = (appConfig?.starsSuggestedPostFutureMin || STARS_SUGGESTED_POST_FUTURE_MIN) * 2;
|
||||||
|
const futureMax = appConfig?.starsSuggestedPostFutureMax || STARS_SUGGESTED_POST_FUTURE_MAX;
|
||||||
|
const scheduleDate = message?.suggestedPostInfo?.scheduleDate;
|
||||||
|
|
||||||
|
return {
|
||||||
|
minAge,
|
||||||
|
futureMin,
|
||||||
|
futureMax,
|
||||||
|
message,
|
||||||
|
sender,
|
||||||
|
isAdmin,
|
||||||
|
commissionPermille,
|
||||||
|
scheduleDate,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(SuggestedPostApprovalModal));
|
||||||
@ -110,6 +110,15 @@ export const TODO_ITEMS_LIMIT = 30;
|
|||||||
export const TODO_TITLE_LENGTH_LIMIT = 32;
|
export const TODO_TITLE_LENGTH_LIMIT = 32;
|
||||||
export const TODO_ITEM_LENGTH_LIMIT = 64;
|
export const TODO_ITEM_LENGTH_LIMIT = 64;
|
||||||
|
|
||||||
|
// Suggested Posts defaults
|
||||||
|
export const STARS_SUGGESTED_POST_AMOUNT_MAX = 100000;
|
||||||
|
export const STARS_SUGGESTED_POST_AMOUNT_MIN = 5;
|
||||||
|
export const STARS_SUGGESTED_POST_COMMISSION_PERMILLE = 850;
|
||||||
|
export const STARS_SUGGESTED_POST_AGE_MIN = 86400; // 24 hours in seconds
|
||||||
|
export const STARS_SUGGESTED_POST_FUTURE_MAX = 2678400; // 31 days in seconds
|
||||||
|
export const STARS_SUGGESTED_POST_FUTURE_MIN = 300; // 5 minutes in seconds
|
||||||
|
export const TON_SUGGESTED_POST_COMMISSION_PERMILLE = 850;
|
||||||
|
|
||||||
export const STORY_VIEWS_MIN_SEARCH = 15;
|
export const STORY_VIEWS_MIN_SEARCH = 15;
|
||||||
export const STORY_MIN_REACTIONS_SORT = 10;
|
export const STORY_MIN_REACTIONS_SORT = 10;
|
||||||
export const STORY_VIEWS_MIN_CONTACTS_FILTER = 20;
|
export const STORY_VIEWS_MIN_CONTACTS_FILTER = 20;
|
||||||
|
|||||||
@ -78,6 +78,23 @@ const TOP_PEERS_REQUEST_COOLDOWN = 60; // 1 min
|
|||||||
const runDebouncedForSearch = debounce((cb) => cb(), 500, false);
|
const runDebouncedForSearch = debounce((cb) => cb(), 500, false);
|
||||||
let botFatherId: string | null;
|
let botFatherId: string | null;
|
||||||
|
|
||||||
|
addActionHandler('clickSuggestedMessageButton', (global, actions, payload): ActionReturnType => {
|
||||||
|
const {
|
||||||
|
chatId, messageId, button, tabId = getCurrentTabId(),
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const { buttonType } = button;
|
||||||
|
const message = selectChatMessage(global, chatId, messageId);
|
||||||
|
|
||||||
|
switch (buttonType) {
|
||||||
|
case 'suggestChanges':
|
||||||
|
if (!message) break;
|
||||||
|
|
||||||
|
actions.initDraftFromSuggestedMessage({ chatId, messageId, tabId });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('clickBotInlineButton', (global, actions, payload): ActionReturnType => {
|
addActionHandler('clickBotInlineButton', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
chatId, messageId, button, tabId = getCurrentTabId(),
|
chatId, messageId, button, tabId = getCurrentTabId(),
|
||||||
|
|||||||
@ -6,10 +6,12 @@ import type {
|
|||||||
ApiError,
|
ApiError,
|
||||||
ApiInputMessageReplyInfo,
|
ApiInputMessageReplyInfo,
|
||||||
ApiInputStoryReplyInfo,
|
ApiInputStoryReplyInfo,
|
||||||
|
ApiInputSuggestedPostInfo,
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
ApiOnProgress,
|
ApiOnProgress,
|
||||||
ApiStory,
|
ApiStory,
|
||||||
ApiUser,
|
ApiUser,
|
||||||
|
MediaContent,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import type {
|
import type {
|
||||||
ForwardMessagesParams,
|
ForwardMessagesParams,
|
||||||
@ -31,6 +33,7 @@ import {
|
|||||||
MESSAGE_LIST_SLICE,
|
MESSAGE_LIST_SLICE,
|
||||||
RE_TELEGRAM_LINK,
|
RE_TELEGRAM_LINK,
|
||||||
SERVICE_NOTIFICATIONS_USER_ID,
|
SERVICE_NOTIFICATIONS_USER_ID,
|
||||||
|
STARS_SUGGESTED_POST_FUTURE_MIN,
|
||||||
SUPPORTED_AUDIO_CONTENT_TYPES,
|
SUPPORTED_AUDIO_CONTENT_TYPES,
|
||||||
SUPPORTED_PHOTO_CONTENT_TYPES,
|
SUPPORTED_PHOTO_CONTENT_TYPES,
|
||||||
SUPPORTED_VIDEO_CONTENT_TYPES,
|
SUPPORTED_VIDEO_CONTENT_TYPES,
|
||||||
@ -124,6 +127,7 @@ import {
|
|||||||
selectIsChatWithSelf,
|
selectIsChatWithSelf,
|
||||||
selectIsCurrentUserFrozen,
|
selectIsCurrentUserFrozen,
|
||||||
selectIsCurrentUserPremium,
|
selectIsCurrentUserPremium,
|
||||||
|
selectIsMonoforumAdmin,
|
||||||
selectLanguageCode,
|
selectLanguageCode,
|
||||||
selectListedIds,
|
selectListedIds,
|
||||||
selectMessageReplyInfo,
|
selectMessageReplyInfo,
|
||||||
@ -135,6 +139,7 @@ import {
|
|||||||
selectPollFromMessage,
|
selectPollFromMessage,
|
||||||
selectRealLastReadId,
|
selectRealLastReadId,
|
||||||
selectReplyCanBeSentToChat,
|
selectReplyCanBeSentToChat,
|
||||||
|
selectSavedDialogIdFromMessage,
|
||||||
selectScheduledMessage,
|
selectScheduledMessage,
|
||||||
selectSendAs,
|
selectSendAs,
|
||||||
selectTabState,
|
selectTabState,
|
||||||
@ -337,6 +342,8 @@ addActionHandler('sendMessage', async (global, actions, payload): Promise<void>
|
|||||||
const isForwarding = selectTabState(global, tabId).forwardMessages?.messageIds?.length;
|
const isForwarding = selectTabState(global, tabId).forwardMessages?.messageIds?.length;
|
||||||
|
|
||||||
const draftReplyInfo = !isForwarding && !isStoryReply ? draft?.replyInfo : undefined;
|
const draftReplyInfo = !isForwarding && !isStoryReply ? draft?.replyInfo : undefined;
|
||||||
|
const draftSuggestedPostInfo = !isForwarding && !isStoryReply
|
||||||
|
? draft?.suggestedPostInfo : undefined;
|
||||||
|
|
||||||
const storyReplyInfo = isStoryReply ? {
|
const storyReplyInfo = isStoryReply ? {
|
||||||
type: 'story',
|
type: 'story',
|
||||||
@ -354,16 +361,41 @@ addActionHandler('sendMessage', async (global, actions, payload): Promise<void>
|
|||||||
|
|
||||||
const messagePriceInStars = await getPeerStarsForMessage(global, chatId!);
|
const messagePriceInStars = await getPeerStarsForMessage(global, chatId!);
|
||||||
|
|
||||||
|
const suggestedPostPrice = draftSuggestedPostInfo?.price?.amount || 0;
|
||||||
|
if (suggestedPostPrice && !draftReplyInfo) {
|
||||||
|
const currentBalance = global.stars?.balance?.amount || 0;
|
||||||
|
|
||||||
|
if (suggestedPostPrice > currentBalance) {
|
||||||
|
actions.openStarsBalanceModal({
|
||||||
|
topup: {
|
||||||
|
balanceNeeded: suggestedPostPrice,
|
||||||
|
},
|
||||||
|
tabId,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const suggestedMessage = draftReplyInfo && draftSuggestedPostInfo
|
||||||
|
? selectChatMessage(global, chatId!, draftReplyInfo.replyToMsgId) : undefined;
|
||||||
|
let suggestedMedia: MediaContent | undefined;
|
||||||
|
if (draftSuggestedPostInfo && suggestedMessage?.content) {
|
||||||
|
suggestedMedia = suggestedMessage.content;
|
||||||
|
}
|
||||||
|
|
||||||
const params: SendMessageParams = {
|
const params: SendMessageParams = {
|
||||||
...payload,
|
...payload,
|
||||||
chat,
|
chat,
|
||||||
replyInfo,
|
replyInfo,
|
||||||
|
suggestedPostInfo: draftSuggestedPostInfo,
|
||||||
|
suggestedMedia,
|
||||||
noWebPage: selectNoWebPage(global, chatId!, threadId!),
|
noWebPage: selectNoWebPage(global, chatId!, threadId!),
|
||||||
sendAs: selectSendAs(global, chatId!),
|
sendAs: selectSendAs(global, chatId!),
|
||||||
lastMessageId,
|
lastMessageId,
|
||||||
messagePriceInStars,
|
messagePriceInStars,
|
||||||
isStoryReply,
|
isStoryReply,
|
||||||
isPending: messagePriceInStars ? true : undefined,
|
isPending: messagePriceInStars ? true : undefined,
|
||||||
|
...suggestedMessage && { isInvertedMedia: suggestedMessage?.isInvertedMedia },
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isStoryReply) {
|
if (!isStoryReply) {
|
||||||
@ -608,7 +640,7 @@ addActionHandler('saveDraft', (global, actions, payload): ActionReturnType => {
|
|||||||
|
|
||||||
const currentDraft = selectDraft(global, chatId, threadId);
|
const currentDraft = selectDraft(global, chatId, threadId);
|
||||||
|
|
||||||
if (chat.isMonoforum && !currentDraft?.replyInfo) {
|
if (chat.isMonoforum && !currentDraft?.replyInfo && !currentDraft?.suggestedPostInfo) {
|
||||||
return; // Monoforum doesn't support drafts outside threads
|
return; // Monoforum doesn't support drafts outside threads
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -616,6 +648,7 @@ addActionHandler('saveDraft', (global, actions, payload): ActionReturnType => {
|
|||||||
text,
|
text,
|
||||||
replyInfo: currentDraft?.replyInfo,
|
replyInfo: currentDraft?.replyInfo,
|
||||||
effectId: currentDraft?.effectId,
|
effectId: currentDraft?.effectId,
|
||||||
|
suggestedPostInfo: currentDraft?.suggestedPostInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
saveDraft({
|
saveDraft({
|
||||||
@ -625,7 +658,7 @@ addActionHandler('saveDraft', (global, actions, payload): ActionReturnType => {
|
|||||||
|
|
||||||
addActionHandler('clearDraft', (global, actions, payload): ActionReturnType => {
|
addActionHandler('clearDraft', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
chatId, threadId = MAIN_THREAD_ID, isLocalOnly, shouldKeepReply,
|
chatId, threadId = MAIN_THREAD_ID, isLocalOnly, shouldKeepReply, shouldKeepSuggestedPost,
|
||||||
} = payload;
|
} = payload;
|
||||||
const currentDraft = selectDraft(global, chatId, threadId);
|
const currentDraft = selectDraft(global, chatId, threadId);
|
||||||
if (!currentDraft) {
|
if (!currentDraft) {
|
||||||
@ -634,8 +667,9 @@ addActionHandler('clearDraft', (global, actions, payload): ActionReturnType => {
|
|||||||
|
|
||||||
const currentReplyInfo = currentDraft.replyInfo;
|
const currentReplyInfo = currentDraft.replyInfo;
|
||||||
|
|
||||||
const newDraft: ApiDraft | undefined = shouldKeepReply && currentReplyInfo ? {
|
const newDraft: ApiDraft | undefined = (shouldKeepReply || shouldKeepSuggestedPost) ? {
|
||||||
replyInfo: currentReplyInfo,
|
replyInfo: shouldKeepReply ? currentReplyInfo : undefined,
|
||||||
|
suggestedPostInfo: shouldKeepSuggestedPost ? currentDraft.suggestedPostInfo : undefined,
|
||||||
} : undefined;
|
} : undefined;
|
||||||
|
|
||||||
saveDraft({
|
saveDraft({
|
||||||
@ -665,6 +699,7 @@ addActionHandler('updateDraftReplyInfo', (global, actions, payload): ActionRetur
|
|||||||
const newDraft: ApiDraft = {
|
const newDraft: ApiDraft = {
|
||||||
...currentDraft,
|
...currentDraft,
|
||||||
replyInfo: updatedReplyInfo,
|
replyInfo: updatedReplyInfo,
|
||||||
|
suggestedPostInfo: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
saveDraft({
|
saveDraft({
|
||||||
@ -682,7 +717,7 @@ addActionHandler('resetDraftReplyInfo', (global, actions, payload): ActionReturn
|
|||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
|
|
||||||
const currentDraft = selectDraft(global, chatId, threadId);
|
const currentDraft = selectDraft(global, chatId, threadId);
|
||||||
if (chat?.isMonoforum && !currentDraft?.replyInfo) {
|
if (chat?.isMonoforum && !currentDraft?.replyInfo && !currentDraft?.suggestedPostInfo) {
|
||||||
return; // Monoforum doesn't support drafts outside threads
|
return; // Monoforum doesn't support drafts outside threads
|
||||||
}
|
}
|
||||||
const newDraft: ApiDraft | undefined = !currentDraft?.text ? undefined : {
|
const newDraft: ApiDraft | undefined = !currentDraft?.text ? undefined : {
|
||||||
@ -695,6 +730,92 @@ addActionHandler('resetDraftReplyInfo', (global, actions, payload): ActionReturn
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('updateDraftSuggestedPostInfo', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { tabId = getCurrentTabId(), ...update } = payload;
|
||||||
|
const currentMessageList = selectCurrentMessageList(global, tabId);
|
||||||
|
if (!currentMessageList) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { chatId, threadId } = currentMessageList;
|
||||||
|
|
||||||
|
const currentDraft = selectDraft(global, chatId, threadId);
|
||||||
|
|
||||||
|
const updatedSuggestedPostInfo = {
|
||||||
|
...currentDraft?.suggestedPostInfo,
|
||||||
|
...update,
|
||||||
|
} as ApiInputSuggestedPostInfo;
|
||||||
|
|
||||||
|
const newDraft: ApiDraft = {
|
||||||
|
...currentDraft,
|
||||||
|
suggestedPostInfo: updatedSuggestedPostInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
saveDraft({
|
||||||
|
global, chatId, threadId, draft: newDraft, isLocalOnly: true, noLocalTimeUpdate: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('resetDraftSuggestedPostInfo', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
const currentMessageList = selectCurrentMessageList(global, tabId);
|
||||||
|
if (!currentMessageList) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { chatId, threadId } = currentMessageList;
|
||||||
|
|
||||||
|
saveDraft({
|
||||||
|
global, chatId, threadId, draft: undefined, isLocalOnly: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('initDraftFromSuggestedMessage', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { chatId, messageId, tabId = getCurrentTabId() } = payload;
|
||||||
|
const message = selectChatMessage(global, chatId, messageId);
|
||||||
|
if (!message) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentMessageList = selectCurrentMessageList(global, tabId);
|
||||||
|
if (!currentMessageList) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { threadId } = currentMessageList;
|
||||||
|
|
||||||
|
actions.clearDraft({
|
||||||
|
chatId,
|
||||||
|
threadId,
|
||||||
|
isLocalOnly: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
actions.updateDraftReplyInfo({
|
||||||
|
replyToMsgId: messageId,
|
||||||
|
monoforumPeerId: selectSavedDialogIdFromMessage(global, message),
|
||||||
|
tabId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (message.suggestedPostInfo) {
|
||||||
|
const { scheduleDate, ...messageSuggestedPost } = message.suggestedPostInfo;
|
||||||
|
const now = getServerTime();
|
||||||
|
const futureMin = global.appConfig?.starsSuggestedPostFutureMin || STARS_SUGGESTED_POST_FUTURE_MIN;
|
||||||
|
|
||||||
|
const validScheduleDate = scheduleDate && scheduleDate > now + futureMin ? scheduleDate : undefined;
|
||||||
|
|
||||||
|
actions.updateDraftSuggestedPostInfo({
|
||||||
|
...messageSuggestedPost,
|
||||||
|
scheduleDate: validScheduleDate,
|
||||||
|
tabId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.saveDraft({
|
||||||
|
chatId,
|
||||||
|
threadId,
|
||||||
|
text: message.content.text,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('saveEffectInDraft', (global, actions, payload): ActionReturnType => {
|
addActionHandler('saveEffectInDraft', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
chatId, threadId, effectId,
|
chatId, threadId, effectId,
|
||||||
@ -702,7 +823,7 @@ addActionHandler('saveEffectInDraft', (global, actions, payload): ActionReturnTy
|
|||||||
|
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
const currentDraft = selectDraft(global, chatId, threadId);
|
const currentDraft = selectDraft(global, chatId, threadId);
|
||||||
if (chat?.isMonoforum && !currentDraft?.replyInfo) {
|
if (chat?.isMonoforum && !currentDraft?.replyInfo && !currentDraft?.suggestedPostInfo) {
|
||||||
return; // Monoforum doesn't support drafts outside threads
|
return; // Monoforum doesn't support drafts outside threads
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1713,6 +1834,9 @@ export async function getPeerStarsForMessage<T extends GlobalState>(
|
|||||||
if (!peer) return undefined;
|
if (!peer) return undefined;
|
||||||
|
|
||||||
if (isApiPeerChat(peer)) {
|
if (isApiPeerChat(peer)) {
|
||||||
|
if (selectIsMonoforumAdmin(global, peerId)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
return peer.paidMessagesStars;
|
return peer.paidMessagesStars;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2068,6 +2192,65 @@ addActionHandler('fetchUnreadMentions', async (global, actions, payload): Promis
|
|||||||
await fetchUnreadMentions(global, chatId, offsetId);
|
await fetchUnreadMentions(global, chatId, offsetId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('approveSuggestedPost', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { chatId, messageId, scheduleDate, tabId = getCurrentTabId() } = payload;
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
if (!chat) return;
|
||||||
|
|
||||||
|
const message = selectChatMessage(global, chatId, messageId);
|
||||||
|
|
||||||
|
const isAdmin = selectIsMonoforumAdmin(global, chatId);
|
||||||
|
|
||||||
|
if (!isAdmin && message?.suggestedPostInfo?.price?.amount) {
|
||||||
|
const neededAmount = message.suggestedPostInfo.price.amount;
|
||||||
|
const currentBalance = global.stars?.balance?.amount || 0;
|
||||||
|
|
||||||
|
if (neededAmount > currentBalance) {
|
||||||
|
actions.openStarsBalanceModal({
|
||||||
|
topup: {
|
||||||
|
balanceNeeded: neededAmount,
|
||||||
|
},
|
||||||
|
tabId,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await callApi('toggleSuggestedPostApproval', {
|
||||||
|
chat,
|
||||||
|
messageId,
|
||||||
|
reject: false,
|
||||||
|
scheduleDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
actions.showNotification({
|
||||||
|
message: { key: 'SuggestedPostApproved' },
|
||||||
|
tabId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('rejectSuggestedPost', async (global, actions, payload): Promise<void> => {
|
||||||
|
const { chatId, messageId, rejectComment, tabId = getCurrentTabId() } = payload;
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
if (!chat) return;
|
||||||
|
|
||||||
|
const result = await callApi('toggleSuggestedPostApproval', {
|
||||||
|
chat,
|
||||||
|
messageId,
|
||||||
|
reject: true,
|
||||||
|
rejectComment,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
actions.showNotification({
|
||||||
|
message: { key: 'SuggestedPostRejectedNotification' },
|
||||||
|
tabId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
async function fetchUnreadMentions<T extends GlobalState>(global: T, chatId: string, offsetId?: number) {
|
async function fetchUnreadMentions<T extends GlobalState>(global: T, chatId: string, offsetId?: number) {
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
if (!chat) return;
|
if (!chat) return;
|
||||||
|
|||||||
@ -99,7 +99,9 @@ addActionHandler('loadStarGifts', async (global): Promise<void> => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const byId = buildCollectionByKey(result, 'id');
|
global = getGlobal();
|
||||||
|
|
||||||
|
const byId = buildCollectionByKey(result.gifts, 'id');
|
||||||
|
|
||||||
const idsByCategoryName: Record<StarGiftCategory, string[]> = {
|
const idsByCategoryName: Record<StarGiftCategory, string[]> = {
|
||||||
all: [],
|
all: [],
|
||||||
@ -134,7 +136,6 @@ addActionHandler('loadStarGifts', async (global): Promise<void> => {
|
|||||||
idsByCategoryName[starsCategory].push(gift.id);
|
idsByCategoryName[starsCategory].push(gift.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
global = getGlobal();
|
|
||||||
global = {
|
global = {
|
||||||
...global,
|
...global,
|
||||||
starGifts: {
|
starGifts: {
|
||||||
|
|||||||
@ -41,6 +41,7 @@ addActionHandler('processOpenChatOrThread', (global, actions, payload): ActionRe
|
|||||||
actions.closeStoryViewer({ tabId });
|
actions.closeStoryViewer({ tabId });
|
||||||
actions.closeStarsBalanceModal({ tabId });
|
actions.closeStarsBalanceModal({ tabId });
|
||||||
actions.closeStarsTransactionModal({ tabId });
|
actions.closeStarsTransactionModal({ tabId });
|
||||||
|
actions.closeGiftInfoModal({ tabId });
|
||||||
|
|
||||||
if (!currentMessageList || (
|
if (!currentMessageList || (
|
||||||
currentMessageList.chatId !== chatId
|
currentMessageList.chatId !== chatId
|
||||||
|
|||||||
@ -1020,6 +1020,34 @@ addActionHandler('closePaidReactionModal', (global, actions, payload): ActionRet
|
|||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addActionHandler('openSuggestMessageModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { chatId, messageId, tabId = getCurrentTabId() } = payload;
|
||||||
|
return updateTabState(global, {
|
||||||
|
suggestMessageModal: { chatId, messageId },
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('closeSuggestMessageModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
return updateTabState(global, {
|
||||||
|
suggestMessageModal: undefined,
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('openSuggestedPostApprovalModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { chatId, messageId, tabId = getCurrentTabId() } = payload;
|
||||||
|
return updateTabState(global, {
|
||||||
|
suggestedPostApprovalModal: { chatId, messageId },
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
addActionHandler('closeSuggestedPostApprovalModal', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
|
return updateTabState(global, {
|
||||||
|
suggestedPostApprovalModal: undefined,
|
||||||
|
}, tabId);
|
||||||
|
});
|
||||||
|
|
||||||
function copyTextForMessages(global: GlobalState, chatId: string, messageIds: number[]) {
|
function copyTextForMessages(global: GlobalState, chatId: string, messageIds: number[]) {
|
||||||
const { type: messageListType, threadId } = selectCurrentMessageList(global) || {};
|
const { type: messageListType, threadId } = selectCurrentMessageList(global) || {};
|
||||||
const lang = langProvider.oldTranslate;
|
const lang = langProvider.oldTranslate;
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import type { TeactNode } from '../../lib/teact/teact';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiAttachment,
|
ApiAttachment,
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
@ -10,6 +12,7 @@ import type {
|
|||||||
ApiPoll, MediaContainer, StatefulMediaContent,
|
ApiPoll, MediaContainer, StatefulMediaContent,
|
||||||
} from '../../api/types/messages';
|
} from '../../api/types/messages';
|
||||||
import type { ThreadId } from '../../types';
|
import type { ThreadId } from '../../types';
|
||||||
|
import type { LangFn } from '../../util/localization';
|
||||||
import type { GlobalState } from '../types';
|
import type { GlobalState } from '../types';
|
||||||
import { ApiMessageEntityTypes, MAIN_THREAD_ID } from '../../api/types';
|
import { ApiMessageEntityTypes, MAIN_THREAD_ID } from '../../api/types';
|
||||||
|
|
||||||
@ -25,6 +28,7 @@ import {
|
|||||||
VERIFICATION_CODES_USER_ID,
|
VERIFICATION_CODES_USER_ID,
|
||||||
VIDEO_STICKER_MIME_TYPE,
|
VIDEO_STICKER_MIME_TYPE,
|
||||||
} from '../../config';
|
} from '../../config';
|
||||||
|
import { areDeepEqual } from '../../util/areDeepEqual';
|
||||||
import { getCleanPeerId, isUserId } from '../../util/entities/ids';
|
import { getCleanPeerId, isUserId } from '../../util/entities/ids';
|
||||||
import { areSortedArraysIntersecting, unique } from '../../util/iteratees';
|
import { areSortedArraysIntersecting, unique } from '../../util/iteratees';
|
||||||
import { isLocalMessageId } from '../../util/keys/messageKey';
|
import { isLocalMessageId } from '../../util/keys/messageKey';
|
||||||
@ -409,3 +413,84 @@ export function splitMessagesForForwarding(messages: ApiMessage[], limit: number
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SuggestedChangesInfo {
|
||||||
|
isNewText: boolean;
|
||||||
|
isNewPrice: boolean;
|
||||||
|
isNewTime: boolean;
|
||||||
|
isNewMedia: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSuggestedChangesInfo(
|
||||||
|
message: ApiMessage,
|
||||||
|
originalMessage?: ApiMessage,
|
||||||
|
): SuggestedChangesInfo | undefined {
|
||||||
|
if (!message.suggestedPostInfo || message.replyInfo?.type !== 'message'
|
||||||
|
|| !message.replyInfo?.replyToMsgId || !originalMessage) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!originalMessage.suggestedPostInfo) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const original = originalMessage.suggestedPostInfo;
|
||||||
|
const suggested = message.suggestedPostInfo;
|
||||||
|
|
||||||
|
const originalContent = originalMessage.content;
|
||||||
|
const suggestedContent = message.content;
|
||||||
|
const { text: originalText, ...originalMediaContent } = originalContent;
|
||||||
|
const { text: suggestedText, ...suggestedMediaContent } = suggestedContent;
|
||||||
|
|
||||||
|
const isNewText = !areDeepEqual(originalText, suggestedText);
|
||||||
|
const isNewMedia = !areDeepEqual(originalMediaContent, suggestedMediaContent);
|
||||||
|
|
||||||
|
const originalPrice = original.price?.amount;
|
||||||
|
const suggestedPrice = suggested.price?.amount;
|
||||||
|
const isNewPrice = originalPrice !== suggestedPrice;
|
||||||
|
|
||||||
|
const originalTime = original.scheduleDate;
|
||||||
|
const suggestedTime = suggested.scheduleDate;
|
||||||
|
const isNewTime = originalTime !== suggestedTime;
|
||||||
|
|
||||||
|
if (!isNewText && !isNewPrice && !isNewTime && !isNewMedia) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isNewText,
|
||||||
|
isNewPrice,
|
||||||
|
isNewTime,
|
||||||
|
isNewMedia,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSuggestedChangesActionText(
|
||||||
|
lang: LangFn,
|
||||||
|
message: ApiMessage,
|
||||||
|
originalMessage?: ApiMessage,
|
||||||
|
isOutgoing?: boolean,
|
||||||
|
senderLink?: TeactNode,
|
||||||
|
): TeactNode | undefined {
|
||||||
|
const changesInfo = getSuggestedChangesInfo(message, originalMessage);
|
||||||
|
if (!changesInfo) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const changesParts: string[] = [];
|
||||||
|
if (changesInfo.isNewPrice) changesParts.push(lang('ActionSuggestedChangesPrice'));
|
||||||
|
if (changesInfo.isNewTime) changesParts.push(lang('ActionSuggestedChangesTime'));
|
||||||
|
if (changesInfo.isNewText) changesParts.push(lang('ActionSuggestedChangesText'));
|
||||||
|
if (changesInfo.isNewMedia) changesParts.push(lang('ActionSuggestedChangesMedia'));
|
||||||
|
|
||||||
|
const changesText = lang.conjunction(changesParts);
|
||||||
|
|
||||||
|
const langKey = isOutgoing ? 'ActionSuggestedChangesOutgoing' : 'ActionSuggestedChangesIncoming';
|
||||||
|
return lang(langKey, {
|
||||||
|
changes: changesText,
|
||||||
|
user: senderLink,
|
||||||
|
}, {
|
||||||
|
withNodes: true,
|
||||||
|
withMarkdown: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@ -18,7 +18,9 @@ import type {
|
|||||||
ApiInputInvoiceStarGift,
|
ApiInputInvoiceStarGift,
|
||||||
ApiInputMessageReplyInfo,
|
ApiInputMessageReplyInfo,
|
||||||
ApiInputSavedStarGift,
|
ApiInputSavedStarGift,
|
||||||
|
ApiInputSuggestedPostInfo,
|
||||||
ApiKeyboardButton,
|
ApiKeyboardButton,
|
||||||
|
ApiKeyboardButtonSuggestedMessage,
|
||||||
ApiLimitTypeWithModal,
|
ApiLimitTypeWithModal,
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
ApiMessageEntity,
|
ApiMessageEntity,
|
||||||
@ -607,6 +609,21 @@ export interface ActionPayloads {
|
|||||||
description?: string;
|
description?: string;
|
||||||
option?: string;
|
option?: string;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
|
approveSuggestedPost: {
|
||||||
|
chatId: string;
|
||||||
|
messageId: number;
|
||||||
|
scheduleDate?: number;
|
||||||
|
} & WithTabId;
|
||||||
|
|
||||||
|
confirmApproveSuggestedPost: {
|
||||||
|
chatId: string;
|
||||||
|
messageId: number;
|
||||||
|
} & WithTabId;
|
||||||
|
rejectSuggestedPost: {
|
||||||
|
chatId: string;
|
||||||
|
messageId: number;
|
||||||
|
rejectComment?: string;
|
||||||
|
} & WithTabId;
|
||||||
sendMessageAction: {
|
sendMessageAction: {
|
||||||
action: ApiSendMessageAction;
|
action: ApiSendMessageAction;
|
||||||
chatId: string;
|
chatId: string;
|
||||||
@ -672,6 +689,7 @@ export interface ActionPayloads {
|
|||||||
threadId?: ThreadId;
|
threadId?: ThreadId;
|
||||||
isLocalOnly?: boolean;
|
isLocalOnly?: boolean;
|
||||||
shouldKeepReply?: boolean;
|
shouldKeepReply?: boolean;
|
||||||
|
shouldKeepSuggestedPost?: boolean;
|
||||||
};
|
};
|
||||||
loadPinnedMessages: {
|
loadPinnedMessages: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
@ -985,6 +1003,12 @@ export interface ActionPayloads {
|
|||||||
focusLastMessage: WithTabId | undefined;
|
focusLastMessage: WithTabId | undefined;
|
||||||
updateDraftReplyInfo: Partial<ApiInputMessageReplyInfo> & WithTabId;
|
updateDraftReplyInfo: Partial<ApiInputMessageReplyInfo> & WithTabId;
|
||||||
resetDraftReplyInfo: WithTabId | undefined;
|
resetDraftReplyInfo: WithTabId | undefined;
|
||||||
|
updateDraftSuggestedPostInfo: Partial<ApiInputSuggestedPostInfo> & WithTabId;
|
||||||
|
resetDraftSuggestedPostInfo: WithTabId | undefined;
|
||||||
|
initDraftFromSuggestedMessage: {
|
||||||
|
chatId: string;
|
||||||
|
messageId: number;
|
||||||
|
} & WithTabId;
|
||||||
updateInsertingPeerIdMention: {
|
updateInsertingPeerIdMention: {
|
||||||
peerId?: string;
|
peerId?: string;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
@ -1997,6 +2021,11 @@ export interface ActionPayloads {
|
|||||||
messageId: number;
|
messageId: number;
|
||||||
button: ApiKeyboardButton;
|
button: ApiKeyboardButton;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
|
clickSuggestedMessageButton: {
|
||||||
|
chatId: string;
|
||||||
|
messageId: number;
|
||||||
|
button: ApiKeyboardButtonSuggestedMessage;
|
||||||
|
} & WithTabId;
|
||||||
|
|
||||||
switchBotInline: {
|
switchBotInline: {
|
||||||
messageId?: number;
|
messageId?: number;
|
||||||
@ -2428,6 +2457,18 @@ export interface ActionPayloads {
|
|||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
closePaidReactionModal: WithTabId | undefined;
|
closePaidReactionModal: WithTabId | undefined;
|
||||||
|
|
||||||
|
openSuggestMessageModal: {
|
||||||
|
chatId: string;
|
||||||
|
messageId?: number;
|
||||||
|
} & WithTabId;
|
||||||
|
closeSuggestMessageModal: WithTabId | undefined;
|
||||||
|
|
||||||
|
openSuggestedPostApprovalModal: {
|
||||||
|
chatId: string;
|
||||||
|
messageId: number;
|
||||||
|
} & WithTabId;
|
||||||
|
closeSuggestedPostApprovalModal: WithTabId | undefined;
|
||||||
|
|
||||||
openDeleteMessageModal: ({
|
openDeleteMessageModal: ({
|
||||||
chatId: string;
|
chatId: string;
|
||||||
messageIds: number[];
|
messageIds: number[];
|
||||||
|
|||||||
@ -758,6 +758,16 @@ export type TabState = {
|
|||||||
messageId: number;
|
messageId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
suggestMessageModal?: {
|
||||||
|
chatId: string;
|
||||||
|
messageId?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
suggestedPostApprovalModal?: {
|
||||||
|
chatId: string;
|
||||||
|
messageId: number;
|
||||||
|
};
|
||||||
|
|
||||||
inviteViaLinkModal?: {
|
inviteViaLinkModal?: {
|
||||||
missingUsers: ApiMissingInvitedUser[];
|
missingUsers: ApiMissingInvitedUser[];
|
||||||
chatId: string;
|
chatId: string;
|
||||||
|
|||||||
@ -12,5 +12,6 @@ for (const tl of Object.values(Api)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LAYER = 205;
|
export const LAYER = 207;
|
||||||
|
|
||||||
export { tlobjects };
|
export { tlobjects };
|
||||||
|
|||||||
363
src/lib/gramjs/tl/api.d.ts
vendored
363
src/lib/gramjs/tl/api.d.ts
vendored
File diff suppressed because one or more lines are too long
@ -93,7 +93,7 @@ chatParticipants#3cbc93f8 chat_id:long participants:Vector<ChatParticipant> vers
|
|||||||
chatPhotoEmpty#37c1011c = ChatPhoto;
|
chatPhotoEmpty#37c1011c = ChatPhoto;
|
||||||
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
|
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
|
||||||
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
|
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
|
||||||
message#eabcdd4d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long = Message;
|
message#9815cec8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true paid_suggested_post_stars:flags2.8?true paid_suggested_post_ton:flags2.9?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long suggested_post:flags2.7?SuggestedPost = Message;
|
||||||
messageService#7a800e0a flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message;
|
messageService#7a800e0a flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message;
|
||||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||||
messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
||||||
@ -166,6 +166,10 @@ messageActionPaidMessagesPrice#84b88578 flags:# broadcast_messages_allowed:flags
|
|||||||
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
|
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
|
||||||
messageActionTodoCompletions#cc7c5c89 completed:Vector<int> incompleted:Vector<int> = MessageAction;
|
messageActionTodoCompletions#cc7c5c89 completed:Vector<int> incompleted:Vector<int> = MessageAction;
|
||||||
messageActionTodoAppendTasks#c7edbc83 list:Vector<TodoItem> = MessageAction;
|
messageActionTodoAppendTasks#c7edbc83 list:Vector<TodoItem> = MessageAction;
|
||||||
|
messageActionSuggestedPostApproval#ee7a1596 flags:# rejected:flags.0?true balance_too_low:flags.1?true reject_comment:flags.2?string schedule_date:flags.3?int price:flags.4?StarsAmount = MessageAction;
|
||||||
|
messageActionSuggestedPostSuccess#95ddcf69 price:StarsAmount = MessageAction;
|
||||||
|
messageActionSuggestedPostRefund#69f916f8 flags:# payer_initiated:flags.0?true = MessageAction;
|
||||||
|
messageActionGiftTon#a8a3c699 flags:# currency:string amount:long crypto_currency:string crypto_amount:long transaction_id:flags.0?string = MessageAction;
|
||||||
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
|
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
|
||||||
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
||||||
photoEmpty#2331b22d id:long = Photo;
|
photoEmpty#2331b22d id:long = Photo;
|
||||||
@ -376,7 +380,6 @@ updateBotNewBusinessMessage#9ddb347c flags:# connection_id:string message:Messag
|
|||||||
updateBotEditBusinessMessage#7df587c flags:# connection_id:string message:Message reply_to_message:flags.0?Message qts:int = Update;
|
updateBotEditBusinessMessage#7df587c flags:# connection_id:string message:Message reply_to_message:flags.0?Message qts:int = Update;
|
||||||
updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector<int> qts:int = Update;
|
updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector<int> qts:int = Update;
|
||||||
updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = Update;
|
updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = Update;
|
||||||
updateBroadcastRevenueTransactions#dfd961f5 peer:Peer balances:BroadcastRevenueBalances = Update;
|
|
||||||
updateStarsBalance#4e80a379 balance:StarsAmount = Update;
|
updateStarsBalance#4e80a379 balance:StarsAmount = Update;
|
||||||
updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update;
|
updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update;
|
||||||
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
|
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
|
||||||
@ -386,6 +389,7 @@ updateSentPhoneCode#504aa18f sent_code:auth.SentCode = Update;
|
|||||||
updateGroupCallChainBlocks#a477288f call:InputGroupCall sub_chain_id:int blocks:Vector<bytes> next_offset:int = Update;
|
updateGroupCallChainBlocks#a477288f call:InputGroupCall sub_chain_id:int blocks:Vector<bytes> next_offset:int = Update;
|
||||||
updateReadMonoForumInbox#77b0e372 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
|
updateReadMonoForumInbox#77b0e372 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
|
||||||
updateReadMonoForumOutbox#a4a79376 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
|
updateReadMonoForumOutbox#a4a79376 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
|
||||||
|
updateMonoForumNoPaidException#9f812b08 flags:# exception:flags.0?true channel_id:long saved_peer_id:Peer = Update;
|
||||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||||
updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference;
|
updates.differenceEmpty#5d75a138 date:int seq:int = updates.Difference;
|
||||||
updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference;
|
updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> state:updates.State = updates.Difference;
|
||||||
@ -549,6 +553,7 @@ inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet;
|
|||||||
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
|
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
|
||||||
inputStickerSetEmojiDefaultTopicIcons#44c1f8e9 = InputStickerSet;
|
inputStickerSetEmojiDefaultTopicIcons#44c1f8e9 = InputStickerSet;
|
||||||
inputStickerSetEmojiChannelDefaultStatuses#49748553 = InputStickerSet;
|
inputStickerSetEmojiChannelDefaultStatuses#49748553 = InputStickerSet;
|
||||||
|
inputStickerSetTonGifts#1cf671a0 = InputStickerSet;
|
||||||
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true emojis:flags.7?true text_color:flags.9?true channel_emoji_status:flags.10?true creator:flags.11?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
|
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true emojis:flags.7?true text_color:flags.9?true channel_emoji_status:flags.10?true creator:flags.11?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
|
||||||
messages.stickerSet#6e153f16 set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = messages.StickerSet;
|
messages.stickerSet#6e153f16 set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = messages.StickerSet;
|
||||||
messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
|
messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
|
||||||
@ -689,7 +694,7 @@ contacts.topPeersNotModified#de266ef5 = contacts.TopPeers;
|
|||||||
contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector<Chat> users:Vector<User> = contacts.TopPeers;
|
contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector<Chat> users:Vector<User> = contacts.TopPeers;
|
||||||
contacts.topPeersDisabled#b52c939d = contacts.TopPeers;
|
contacts.topPeersDisabled#b52c939d = contacts.TopPeers;
|
||||||
draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
|
draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
|
||||||
draftMessage#2d65321f flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo message:string entities:flags.3?Vector<MessageEntity> media:flags.5?InputMedia date:int effect:flags.7?long = DraftMessage;
|
draftMessage#96eaa5eb flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo message:string entities:flags.3?Vector<MessageEntity> media:flags.5?InputMedia date:int effect:flags.7?long suggested_post:flags.8?SuggestedPost = DraftMessage;
|
||||||
messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers;
|
messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers;
|
||||||
messages.featuredStickers#be382906 flags:# premium:flags.0?true hash:long count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
|
messages.featuredStickers#be382906 flags:# premium:flags.0?true hash:long count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
|
||||||
messages.recentStickersNotModified#b17f890 = messages.RecentStickers;
|
messages.recentStickersNotModified#b17f890 = messages.RecentStickers;
|
||||||
@ -966,7 +971,7 @@ pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true correct:flags.1?true optio
|
|||||||
pollResults#7adf2420 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<Peer> solution:flags.4?string solution_entities:flags.4?Vector<MessageEntity> = PollResults;
|
pollResults#7adf2420 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<Peer> solution:flags.4?string solution_entities:flags.4?Vector<MessageEntity> = PollResults;
|
||||||
chatOnlines#f041e250 onlines:int = ChatOnlines;
|
chatOnlines#f041e250 onlines:int = ChatOnlines;
|
||||||
statsURL#47a971e0 url:string = StatsURL;
|
statsURL#47a971e0 url:string = StatsURL;
|
||||||
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true manage_topics:flags.13?true post_stories:flags.14?true edit_stories:flags.15?true delete_stories:flags.16?true = ChatAdminRights;
|
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true manage_topics:flags.13?true post_stories:flags.14?true edit_stories:flags.15?true delete_stories:flags.16?true manage_direct_messages:flags.17?true = ChatAdminRights;
|
||||||
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true manage_topics:flags.18?true send_photos:flags.19?true send_videos:flags.20?true send_roundvideos:flags.21?true send_audios:flags.22?true send_voices:flags.23?true send_docs:flags.24?true send_plain:flags.25?true until_date:int = ChatBannedRights;
|
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true manage_topics:flags.18?true send_photos:flags.19?true send_videos:flags.20?true send_roundvideos:flags.21?true send_audios:flags.22?true send_voices:flags.23?true send_docs:flags.24?true send_plain:flags.25?true until_date:int = ChatBannedRights;
|
||||||
inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper;
|
inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper;
|
||||||
inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
|
inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
|
||||||
@ -1346,16 +1351,9 @@ sponsoredMessageReportOption#430d3150 text:string option:bytes = SponsoredMessag
|
|||||||
channels.sponsoredMessageReportResultChooseOption#846f9e42 title:string options:Vector<SponsoredMessageReportOption> = channels.SponsoredMessageReportResult;
|
channels.sponsoredMessageReportResultChooseOption#846f9e42 title:string options:Vector<SponsoredMessageReportOption> = channels.SponsoredMessageReportResult;
|
||||||
channels.sponsoredMessageReportResultAdsHidden#3e3bcf2f = channels.SponsoredMessageReportResult;
|
channels.sponsoredMessageReportResultAdsHidden#3e3bcf2f = channels.SponsoredMessageReportResult;
|
||||||
channels.sponsoredMessageReportResultReported#ad798849 = channels.SponsoredMessageReportResult;
|
channels.sponsoredMessageReportResultReported#ad798849 = channels.SponsoredMessageReportResult;
|
||||||
stats.broadcastRevenueStats#5407e297 top_hours_graph:StatsGraph revenue_graph:StatsGraph balances:BroadcastRevenueBalances usd_rate:double = stats.BroadcastRevenueStats;
|
|
||||||
stats.broadcastRevenueWithdrawalUrl#ec659737 url:string = stats.BroadcastRevenueWithdrawalUrl;
|
|
||||||
broadcastRevenueTransactionProceeds#557e2cc4 amount:long from_date:int to_date:int = BroadcastRevenueTransaction;
|
|
||||||
broadcastRevenueTransactionWithdrawal#5a590978 flags:# pending:flags.0?true failed:flags.2?true amount:long date:int provider:string transaction_date:flags.1?int transaction_url:flags.1?string = BroadcastRevenueTransaction;
|
|
||||||
broadcastRevenueTransactionRefund#42d30d2e amount:long date:int provider:string = BroadcastRevenueTransaction;
|
|
||||||
stats.broadcastRevenueTransactions#87158466 count:int transactions:Vector<BroadcastRevenueTransaction> = stats.BroadcastRevenueTransactions;
|
|
||||||
reactionNotificationsFromContacts#bac3a61a = ReactionNotificationsFrom;
|
reactionNotificationsFromContacts#bac3a61a = ReactionNotificationsFrom;
|
||||||
reactionNotificationsFromAll#4b9e22a0 = ReactionNotificationsFrom;
|
reactionNotificationsFromAll#4b9e22a0 = ReactionNotificationsFrom;
|
||||||
reactionsNotifySettings#56e34970 flags:# messages_notify_from:flags.0?ReactionNotificationsFrom stories_notify_from:flags.1?ReactionNotificationsFrom sound:NotificationSound show_previews:Bool = ReactionsNotifySettings;
|
reactionsNotifySettings#56e34970 flags:# messages_notify_from:flags.0?ReactionNotificationsFrom stories_notify_from:flags.1?ReactionNotificationsFrom sound:NotificationSound show_previews:Bool = ReactionsNotifySettings;
|
||||||
broadcastRevenueBalances#c3ff71e7 flags:# withdrawal_enabled:flags.0?true current_balance:long available_balance:long overall_revenue:long = BroadcastRevenueBalances;
|
|
||||||
availableEffect#93c3e27e flags:# premium_required:flags.2?true id:long emoticon:string static_icon_id:flags.0?long effect_sticker_id:long effect_animation_id:flags.1?long = AvailableEffect;
|
availableEffect#93c3e27e flags:# premium_required:flags.2?true id:long emoticon:string static_icon_id:flags.0?long effect_sticker_id:long effect_animation_id:flags.1?long = AvailableEffect;
|
||||||
messages.availableEffectsNotModified#d1ed9a5b = messages.AvailableEffects;
|
messages.availableEffectsNotModified#d1ed9a5b = messages.AvailableEffects;
|
||||||
messages.availableEffects#bddb616e hash:int effects:Vector<AvailableEffect> documents:Vector<Document> = messages.AvailableEffects;
|
messages.availableEffects#bddb616e hash:int effects:Vector<AvailableEffect> documents:Vector<Document> = messages.AvailableEffects;
|
||||||
@ -1369,13 +1367,13 @@ starsTransactionPeer#d80da15d peer:Peer = StarsTransactionPeer;
|
|||||||
starsTransactionPeerAds#60682812 = StarsTransactionPeer;
|
starsTransactionPeerAds#60682812 = StarsTransactionPeer;
|
||||||
starsTransactionPeerAPI#f9677aad = StarsTransactionPeer;
|
starsTransactionPeerAPI#f9677aad = StarsTransactionPeer;
|
||||||
starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption;
|
starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption;
|
||||||
starsTransaction#a39fd94a flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true stargift_upgrade:flags.18?true business_transfer:flags.21?true stargift_resale:flags.22?true id:string stars:StarsAmount date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector<MessageMedia> subscription_period:flags.12?int giveaway_post_id:flags.13?int stargift:flags.14?StarGift floodskip_number:flags.15?int starref_commission_permille:flags.16?int starref_peer:flags.17?Peer starref_amount:flags.17?StarsAmount paid_messages:flags.19?int premium_gift_months:flags.20?int = StarsTransaction;
|
starsTransaction#13659eb0 flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true stargift_upgrade:flags.18?true business_transfer:flags.21?true stargift_resale:flags.22?true id:string amount:StarsAmount date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector<MessageMedia> subscription_period:flags.12?int giveaway_post_id:flags.13?int stargift:flags.14?StarGift floodskip_number:flags.15?int starref_commission_permille:flags.16?int starref_peer:flags.17?Peer starref_amount:flags.17?StarsAmount paid_messages:flags.19?int premium_gift_months:flags.20?int ads_proceeds_from_date:flags.23?int ads_proceeds_to_date:flags.23?int = StarsTransaction;
|
||||||
payments.starsStatus#6c9ce8ed flags:# balance:StarsAmount subscriptions:flags.1?Vector<StarsSubscription> subscriptions_next_offset:flags.2?string subscriptions_missing_balance:flags.4?long history:flags.3?Vector<StarsTransaction> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = payments.StarsStatus;
|
payments.starsStatus#6c9ce8ed flags:# balance:StarsAmount subscriptions:flags.1?Vector<StarsSubscription> subscriptions_next_offset:flags.2?string subscriptions_missing_balance:flags.4?long history:flags.3?Vector<StarsTransaction> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = payments.StarsStatus;
|
||||||
foundStory#e87acbc0 peer:Peer story:StoryItem = FoundStory;
|
foundStory#e87acbc0 peer:Peer story:StoryItem = FoundStory;
|
||||||
stories.foundStories#e2de7737 flags:# count:int stories:Vector<FoundStory> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = stories.FoundStories;
|
stories.foundStories#e2de7737 flags:# count:int stories:Vector<FoundStory> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = stories.FoundStories;
|
||||||
geoPointAddress#de4c5d93 flags:# country_iso2:string state:flags.0?string city:flags.1?string street:flags.2?string = GeoPointAddress;
|
geoPointAddress#de4c5d93 flags:# country_iso2:string state:flags.0?string city:flags.1?string street:flags.2?string = GeoPointAddress;
|
||||||
starsRevenueStatus#febe5491 flags:# withdrawal_enabled:flags.0?true current_balance:StarsAmount available_balance:StarsAmount overall_revenue:StarsAmount next_withdrawal_at:flags.1?int = StarsRevenueStatus;
|
starsRevenueStatus#febe5491 flags:# withdrawal_enabled:flags.0?true current_balance:StarsAmount available_balance:StarsAmount overall_revenue:StarsAmount next_withdrawal_at:flags.1?int = StarsRevenueStatus;
|
||||||
payments.starsRevenueStats#c92bb73b revenue_graph:StatsGraph status:StarsRevenueStatus usd_rate:double = payments.StarsRevenueStats;
|
payments.starsRevenueStats#6c207376 flags:# top_hours_graph:flags.0?StatsGraph revenue_graph:StatsGraph status:StarsRevenueStatus usd_rate:double = payments.StarsRevenueStats;
|
||||||
payments.starsRevenueWithdrawalUrl#1dab80b7 url:string = payments.StarsRevenueWithdrawalUrl;
|
payments.starsRevenueWithdrawalUrl#1dab80b7 url:string = payments.StarsRevenueWithdrawalUrl;
|
||||||
payments.starsRevenueAdsAccountUrl#394e7f21 url:string = payments.StarsRevenueAdsAccountUrl;
|
payments.starsRevenueAdsAccountUrl#394e7f21 url:string = payments.StarsRevenueAdsAccountUrl;
|
||||||
inputStarsTransaction#206ae6d1 flags:# refund:flags.0?true id:string = InputStarsTransaction;
|
inputStarsTransaction#206ae6d1 flags:# refund:flags.0?true id:string = InputStarsTransaction;
|
||||||
@ -1388,10 +1386,10 @@ starsSubscription#2e6eab1a flags:# canceled:flags.0?true can_refulfill:flags.1?t
|
|||||||
messageReactor#4ba3a95a flags:# top:flags.0?true my:flags.1?true anonymous:flags.2?true peer_id:flags.3?Peer count:int = MessageReactor;
|
messageReactor#4ba3a95a flags:# top:flags.0?true my:flags.1?true anonymous:flags.2?true peer_id:flags.3?Peer count:int = MessageReactor;
|
||||||
starsGiveawayOption#94ce852a flags:# extended:flags.0?true default:flags.1?true stars:long yearly_boosts:int store_product:flags.2?string currency:string amount:long winners:Vector<StarsGiveawayWinnersOption> = StarsGiveawayOption;
|
starsGiveawayOption#94ce852a flags:# extended:flags.0?true default:flags.1?true stars:long yearly_boosts:int store_product:flags.2?string currency:string amount:long winners:Vector<StarsGiveawayWinnersOption> = StarsGiveawayOption;
|
||||||
starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption;
|
starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption;
|
||||||
starGift#c62aca28 flags:# limited:flags.0?true sold_out:flags.1?true birthday:flags.2?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int availability_resale:flags.4?long convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int upgrade_stars:flags.3?long resell_min_stars:flags.4?long title:flags.5?string = StarGift;
|
starGift#7f853c12 flags:# limited:flags.0?true sold_out:flags.1?true birthday:flags.2?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int availability_resale:flags.4?long convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int upgrade_stars:flags.3?long resell_min_stars:flags.4?long title:flags.5?string released_by:flags.6?Peer = StarGift;
|
||||||
starGiftUnique#6411db89 flags:# id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int gift_address:flags.3?string resell_stars:flags.4?long = StarGift;
|
starGiftUnique#f63778ae flags:# id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int gift_address:flags.3?string resell_stars:flags.4?long released_by:flags.5?Peer = StarGift;
|
||||||
payments.starGiftsNotModified#a388a368 = payments.StarGifts;
|
payments.starGiftsNotModified#a388a368 = payments.StarGifts;
|
||||||
payments.starGifts#901689ea hash:int gifts:Vector<StarGift> = payments.StarGifts;
|
payments.starGifts#2ed82995 hash:int gifts:Vector<StarGift> chats:Vector<Chat> users:Vector<User> = payments.StarGifts;
|
||||||
messageReportOption#7903e3d9 text:string option:bytes = MessageReportOption;
|
messageReportOption#7903e3d9 text:string option:bytes = MessageReportOption;
|
||||||
reportResultChooseOption#f0e4e0b6 title:string options:Vector<MessageReportOption> = ReportResult;
|
reportResultChooseOption#f0e4e0b6 title:string options:Vector<MessageReportOption> = ReportResult;
|
||||||
reportResultAddComment#6f09ac31 flags:# optional:flags.0?true option:bytes = ReportResult;
|
reportResultAddComment#6f09ac31 flags:# optional:flags.0?true option:bytes = ReportResult;
|
||||||
@ -1404,6 +1402,7 @@ connectedBotStarRef#19a13f71 flags:# revoked:flags.1?true url:string date:int bo
|
|||||||
payments.connectedStarRefBots#98d5ea1d count:int connected_bots:Vector<ConnectedBotStarRef> users:Vector<User> = payments.ConnectedStarRefBots;
|
payments.connectedStarRefBots#98d5ea1d count:int connected_bots:Vector<ConnectedBotStarRef> users:Vector<User> = payments.ConnectedStarRefBots;
|
||||||
payments.suggestedStarRefBots#b4d5d859 flags:# count:int suggested_bots:Vector<StarRefProgram> users:Vector<User> next_offset:flags.0?string = payments.SuggestedStarRefBots;
|
payments.suggestedStarRefBots#b4d5d859 flags:# count:int suggested_bots:Vector<StarRefProgram> users:Vector<User> next_offset:flags.0?string = payments.SuggestedStarRefBots;
|
||||||
starsAmount#bbb6b4a3 amount:long nanos:int = StarsAmount;
|
starsAmount#bbb6b4a3 amount:long nanos:int = StarsAmount;
|
||||||
|
starsTonAmount#74aee3e0 amount:long = StarsAmount;
|
||||||
messages.foundStickersNotModified#6010c534 flags:# next_offset:flags.0?int = messages.FoundStickers;
|
messages.foundStickersNotModified#6010c534 flags:# next_offset:flags.0?int = messages.FoundStickers;
|
||||||
messages.foundStickers#82c9e290 flags:# next_offset:flags.0?int hash:long stickers:Vector<Document> = messages.FoundStickers;
|
messages.foundStickers#82c9e290 flags:# next_offset:flags.0?int hash:long stickers:Vector<Document> = messages.FoundStickers;
|
||||||
botVerifierSettings#b0cd6617 flags:# can_modify_custom_description:flags.1?true icon:long company:string custom_description:flags.0?string = BotVerifierSettings;
|
botVerifierSettings#b0cd6617 flags:# can_modify_custom_description:flags.1?true icon:long company:string custom_description:flags.0?string = BotVerifierSettings;
|
||||||
@ -1445,6 +1444,7 @@ pendingSuggestion#e7e82e12 suggestion:string title:TextWithEntities description:
|
|||||||
todoItem#cba9a52f id:int title:TextWithEntities = TodoItem;
|
todoItem#cba9a52f id:int title:TextWithEntities = TodoItem;
|
||||||
todoList#49b92a26 flags:# others_can_append:flags.0?true others_can_complete:flags.1?true title:TextWithEntities list:Vector<TodoItem> = TodoList;
|
todoList#49b92a26 flags:# others_can_append:flags.0?true others_can_complete:flags.1?true title:TextWithEntities list:Vector<TodoItem> = TodoList;
|
||||||
todoCompletion#4cc120b7 id:int completed_by:long date:int = TodoCompletion;
|
todoCompletion#4cc120b7 id:int completed_by:long date:int = TodoCompletion;
|
||||||
|
suggestedPost#e8e37e5 flags:# accepted:flags.1?true rejected:flags.2?true price:flags.3?StarsAmount schedule_date:flags.0?int = SuggestedPost;
|
||||||
---functions---
|
---functions---
|
||||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||||
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
|
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
|
||||||
@ -1511,6 +1511,7 @@ account.resolveBusinessChatLink#5492e5ee slug:string = account.ResolvedBusinessC
|
|||||||
account.toggleSponsoredMessages#b9d9a38d enabled:Bool = Bool;
|
account.toggleSponsoredMessages#b9d9a38d enabled:Bool = Bool;
|
||||||
account.getCollectibleEmojiStatuses#2e7b4543 hash:long = account.EmojiStatuses;
|
account.getCollectibleEmojiStatuses#2e7b4543 hash:long = account.EmojiStatuses;
|
||||||
account.getPaidMessagesRevenue#19ba4a67 flags:# parent_peer:flags.0?InputPeer user_id:InputUser = account.PaidMessagesRevenue;
|
account.getPaidMessagesRevenue#19ba4a67 flags:# parent_peer:flags.0?InputPeer user_id:InputUser = account.PaidMessagesRevenue;
|
||||||
|
account.toggleNoPaidMessagesException#fe2eda76 flags:# refund_charged:flags.0?true require_payment:flags.2?true parent_peer:flags.1?InputPeer user_id:InputUser = Bool;
|
||||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||||
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
|
||||||
contacts.getContacts#5dd69e12 hash:long = contacts.Contacts;
|
contacts.getContacts#5dd69e12 hash:long = contacts.Contacts;
|
||||||
@ -1535,9 +1536,9 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t
|
|||||||
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
|
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
|
||||||
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
||||||
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
|
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
|
||||||
messages.sendMessage#fbf2340a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates;
|
messages.sendMessage#fe05dc9a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long suggested_post:flags.22?SuggestedPost = Updates;
|
||||||
messages.sendMedia#a550cd78 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates;
|
messages.sendMedia#ac55d9c1 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long suggested_post:flags.22?SuggestedPost = Updates;
|
||||||
messages.forwardMessages#38f0188c flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true allow_paid_floodskip:flags.19?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int reply_to:flags.22?InputReplyTo schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut video_timestamp:flags.20?int allow_paid_stars:flags.21?long = Updates;
|
messages.forwardMessages#978928ca flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true allow_paid_floodskip:flags.19?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int reply_to:flags.22?InputReplyTo schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut video_timestamp:flags.20?int allow_paid_stars:flags.21?long suggested_post:flags.23?SuggestedPost = Updates;
|
||||||
messages.reportSpam#cf1592db peer:InputPeer = Bool;
|
messages.reportSpam#cf1592db peer:InputPeer = Bool;
|
||||||
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
|
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
|
||||||
messages.report#fc78af9b peer:InputPeer id:Vector<int> option:bytes message:string = ReportResult;
|
messages.report#fc78af9b peer:InputPeer id:Vector<int> option:bytes message:string = ReportResult;
|
||||||
@ -1571,7 +1572,7 @@ messages.sendInlineBotResult#c0cf7646 flags:# silent:flags.5?true background:fla
|
|||||||
messages.editMessage#dfd14005 flags:# no_webpage:flags.1?true invert_media:flags.16?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int quick_reply_shortcut_id:flags.17?int = Updates;
|
messages.editMessage#dfd14005 flags:# no_webpage:flags.1?true invert_media:flags.16?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int quick_reply_shortcut_id:flags.17?int = Updates;
|
||||||
messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
|
messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
|
||||||
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
|
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
|
||||||
messages.saveDraft#d372c5ce flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> media:flags.5?InputMedia effect:flags.7?long = Bool;
|
messages.saveDraft#54ae308e flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> media:flags.5?InputMedia effect:flags.7?long suggested_post:flags.8?SuggestedPost = Bool;
|
||||||
messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers;
|
messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers;
|
||||||
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
|
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
|
||||||
messages.getRecentStickers#9da9403b flags:# attached:flags.0?true hash:long = messages.RecentStickers;
|
messages.getRecentStickers#9da9403b flags:# attached:flags.0?true hash:long = messages.RecentStickers;
|
||||||
@ -1674,6 +1675,7 @@ messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.Pr
|
|||||||
messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool;
|
messages.reportMessagesDelivery#5a6d7395 flags:# push:flags.0?true peer:InputPeer id:Vector<int> = Bool;
|
||||||
messages.toggleTodoCompleted#d3e03124 peer:InputPeer msg_id:int completed:Vector<int> incompleted:Vector<int> = Updates;
|
messages.toggleTodoCompleted#d3e03124 peer:InputPeer msg_id:int completed:Vector<int> incompleted:Vector<int> = Updates;
|
||||||
messages.appendTodoList#21a61057 peer:InputPeer msg_id:int list:Vector<TodoItem> = Updates;
|
messages.appendTodoList#21a61057 peer:InputPeer msg_id:int list:Vector<TodoItem> = Updates;
|
||||||
|
messages.toggleSuggestedPostApproval#8107455c flags:# reject:flags.1?true peer:InputPeer msg_id:int schedule_date:flags.0?int reject_comment:flags.2?string = Updates;
|
||||||
updates.getState#edd4882a = updates.State;
|
updates.getState#edd4882a = updates.State;
|
||||||
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
|
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
|
||||||
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
||||||
@ -1760,11 +1762,11 @@ payments.applyGiftCode#f6e26854 slug:string = Updates;
|
|||||||
payments.getGiveawayInfo#f4239425 peer:InputPeer msg_id:int = payments.GiveawayInfo;
|
payments.getGiveawayInfo#f4239425 peer:InputPeer msg_id:int = payments.GiveawayInfo;
|
||||||
payments.launchPrepaidGiveaway#5ff58f20 peer:InputPeer giveaway_id:long purpose:InputStorePaymentPurpose = Updates;
|
payments.launchPrepaidGiveaway#5ff58f20 peer:InputPeer giveaway_id:long purpose:InputStorePaymentPurpose = Updates;
|
||||||
payments.getStarsTopupOptions#c00ec7d3 = Vector<StarsTopupOption>;
|
payments.getStarsTopupOptions#c00ec7d3 = Vector<StarsTopupOption>;
|
||||||
payments.getStarsStatus#104fcfa7 peer:InputPeer = payments.StarsStatus;
|
payments.getStarsStatus#4ea9b3bf flags:# ton:flags.0?true peer:InputPeer = payments.StarsStatus;
|
||||||
payments.getStarsTransactions#69da4557 flags:# inbound:flags.0?true outbound:flags.1?true ascending:flags.2?true subscription_id:flags.3?string peer:InputPeer offset:string limit:int = payments.StarsStatus;
|
payments.getStarsTransactions#69da4557 flags:# inbound:flags.0?true outbound:flags.1?true ascending:flags.2?true ton:flags.4?true subscription_id:flags.3?string peer:InputPeer offset:string limit:int = payments.StarsStatus;
|
||||||
payments.sendStarsForm#7998c914 form_id:long invoice:InputInvoice = payments.PaymentResult;
|
payments.sendStarsForm#7998c914 form_id:long invoice:InputInvoice = payments.PaymentResult;
|
||||||
payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates;
|
payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates;
|
||||||
payments.getStarsTransactionsByID#27842d2e peer:InputPeer id:Vector<InputStarsTransaction> = payments.StarsStatus;
|
payments.getStarsTransactionsByID#2dca16b8 flags:# ton:flags.0?true peer:InputPeer id:Vector<InputStarsTransaction> = payments.StarsStatus;
|
||||||
payments.getStarsGiftOptions#d3c96bc8 flags:# user_id:flags.0?InputUser = Vector<StarsGiftOption>;
|
payments.getStarsGiftOptions#d3c96bc8 flags:# user_id:flags.0?InputUser = Vector<StarsGiftOption>;
|
||||||
payments.getStarsSubscriptions#32512c5 flags:# missing_balance:flags.0?true peer:InputPeer offset:string = payments.StarsStatus;
|
payments.getStarsSubscriptions#32512c5 flags:# missing_balance:flags.0?true peer:InputPeer offset:string = payments.StarsStatus;
|
||||||
payments.changeStarsSubscription#c7770878 flags:# peer:InputPeer subscription_id:string canceled:flags.0?Bool = Bool;
|
payments.changeStarsSubscription#c7770878 flags:# peer:InputPeer subscription_id:string canceled:flags.0?Bool = Bool;
|
||||||
@ -1814,8 +1816,6 @@ stats.getMessagePublicForwards#5f150144 channel:InputChannel msg_id:int offset:s
|
|||||||
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
|
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
|
||||||
stats.getStoryStats#374fef40 flags:# dark:flags.0?true peer:InputPeer id:int = stats.StoryStats;
|
stats.getStoryStats#374fef40 flags:# dark:flags.0?true peer:InputPeer id:int = stats.StoryStats;
|
||||||
stats.getStoryPublicForwards#a6437ef6 peer:InputPeer id:int offset:string limit:int = stats.PublicForwards;
|
stats.getStoryPublicForwards#a6437ef6 peer:InputPeer id:int offset:string limit:int = stats.PublicForwards;
|
||||||
stats.getBroadcastRevenueStats#f788ee19 flags:# dark:flags.0?true peer:InputPeer = stats.BroadcastRevenueStats;
|
|
||||||
stats.getBroadcastRevenueWithdrawalUrl#9df4faad peer:InputPeer password:InputCheckPasswordSRP = stats.BroadcastRevenueWithdrawalUrl;
|
|
||||||
chatlists.exportChatlistInvite#8472478e chatlist:InputChatlist title:string peers:Vector<InputPeer> = chatlists.ExportedChatlistInvite;
|
chatlists.exportChatlistInvite#8472478e chatlist:InputChatlist title:string peers:Vector<InputPeer> = chatlists.ExportedChatlistInvite;
|
||||||
chatlists.deleteExportedInvite#719c5c5e chatlist:InputChatlist slug:string = Bool;
|
chatlists.deleteExportedInvite#719c5c5e chatlist:InputChatlist slug:string = Bool;
|
||||||
chatlists.editExportedInvite#653db63d flags:# chatlist:InputChatlist slug:string title:flags.1?string peers:flags.2?Vector<InputPeer> = ExportedChatlistInvite;
|
chatlists.editExportedInvite#653db63d flags:# chatlist:InputChatlist slug:string title:flags.1?string peers:flags.2?Vector<InputPeer> = ExportedChatlistInvite;
|
||||||
|
|||||||
@ -61,6 +61,7 @@
|
|||||||
"account.resolveBusinessChatLink",
|
"account.resolveBusinessChatLink",
|
||||||
"account.toggleSponsoredMessages",
|
"account.toggleSponsoredMessages",
|
||||||
"account.getCollectibleEmojiStatuses",
|
"account.getCollectibleEmojiStatuses",
|
||||||
|
"account.toggleNoPaidMessagesException",
|
||||||
"account.toggleNoPaidMessagesException ",
|
"account.toggleNoPaidMessagesException ",
|
||||||
"account.getPaidMessagesRevenue",
|
"account.getPaidMessagesRevenue",
|
||||||
"account.getAccountTTL",
|
"account.getAccountTTL",
|
||||||
@ -225,6 +226,7 @@
|
|||||||
"messages.getSponsoredMessages",
|
"messages.getSponsoredMessages",
|
||||||
"messages.reportMessagesDelivery",
|
"messages.reportMessagesDelivery",
|
||||||
"messages.getPreparedInlineMessage",
|
"messages.getPreparedInlineMessage",
|
||||||
|
"messages.toggleSuggestedPostApproval",
|
||||||
"messages.toggleTodoCompleted",
|
"messages.toggleTodoCompleted",
|
||||||
"messages.appendTodoList",
|
"messages.appendTodoList",
|
||||||
"updates.getState",
|
"updates.getState",
|
||||||
|
|||||||
@ -117,7 +117,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
|
|||||||
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
|
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
|
||||||
|
|
||||||
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
|
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
|
||||||
message#eabcdd4d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long = Message;
|
message#9815cec8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true video_processing_pending:flags2.4?true paid_suggested_post_stars:flags2.8?true paid_suggested_post_ton:flags2.9?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck report_delivery_until_date:flags2.5?int paid_message_stars:flags2.6?long suggested_post:flags2.7?SuggestedPost = Message;
|
||||||
messageService#7a800e0a flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message;
|
messageService#7a800e0a flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true reactions_are_possible:flags.9?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer saved_peer_id:flags.28?Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction reactions:flags.20?MessageReactions ttl_period:flags.25?int = Message;
|
||||||
|
|
||||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||||
@ -192,6 +192,10 @@ messageActionPaidMessagesPrice#84b88578 flags:# broadcast_messages_allowed:flags
|
|||||||
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
|
messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector<Peer> = MessageAction;
|
||||||
messageActionTodoCompletions#cc7c5c89 completed:Vector<int> incompleted:Vector<int> = MessageAction;
|
messageActionTodoCompletions#cc7c5c89 completed:Vector<int> incompleted:Vector<int> = MessageAction;
|
||||||
messageActionTodoAppendTasks#c7edbc83 list:Vector<TodoItem> = MessageAction;
|
messageActionTodoAppendTasks#c7edbc83 list:Vector<TodoItem> = MessageAction;
|
||||||
|
messageActionSuggestedPostApproval#ee7a1596 flags:# rejected:flags.0?true balance_too_low:flags.1?true reject_comment:flags.2?string schedule_date:flags.3?int price:flags.4?StarsAmount = MessageAction;
|
||||||
|
messageActionSuggestedPostSuccess#95ddcf69 price:StarsAmount = MessageAction;
|
||||||
|
messageActionSuggestedPostRefund#69f916f8 flags:# payer_initiated:flags.0?true = MessageAction;
|
||||||
|
messageActionGiftTon#a8a3c699 flags:# currency:string amount:long crypto_currency:string crypto_amount:long transaction_id:flags.0?string = MessageAction;
|
||||||
|
|
||||||
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
|
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
|
||||||
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
||||||
@ -429,7 +433,6 @@ updateBotNewBusinessMessage#9ddb347c flags:# connection_id:string message:Messag
|
|||||||
updateBotEditBusinessMessage#7df587c flags:# connection_id:string message:Message reply_to_message:flags.0?Message qts:int = Update;
|
updateBotEditBusinessMessage#7df587c flags:# connection_id:string message:Message reply_to_message:flags.0?Message qts:int = Update;
|
||||||
updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector<int> qts:int = Update;
|
updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector<int> qts:int = Update;
|
||||||
updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = Update;
|
updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = Update;
|
||||||
updateBroadcastRevenueTransactions#dfd961f5 peer:Peer balances:BroadcastRevenueBalances = Update;
|
|
||||||
updateStarsBalance#4e80a379 balance:StarsAmount = Update;
|
updateStarsBalance#4e80a379 balance:StarsAmount = Update;
|
||||||
updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update;
|
updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update;
|
||||||
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
|
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
|
||||||
@ -439,6 +442,7 @@ updateSentPhoneCode#504aa18f sent_code:auth.SentCode = Update;
|
|||||||
updateGroupCallChainBlocks#a477288f call:InputGroupCall sub_chain_id:int blocks:Vector<bytes> next_offset:int = Update;
|
updateGroupCallChainBlocks#a477288f call:InputGroupCall sub_chain_id:int blocks:Vector<bytes> next_offset:int = Update;
|
||||||
updateReadMonoForumInbox#77b0e372 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
|
updateReadMonoForumInbox#77b0e372 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
|
||||||
updateReadMonoForumOutbox#a4a79376 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
|
updateReadMonoForumOutbox#a4a79376 channel_id:long saved_peer_id:Peer read_max_id:int = Update;
|
||||||
|
updateMonoForumNoPaidException#9f812b08 flags:# exception:flags.0?true channel_id:long saved_peer_id:Peer = Update;
|
||||||
|
|
||||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||||
|
|
||||||
@ -648,6 +652,7 @@ inputStickerSetEmojiGenericAnimations#4c4d4ce = InputStickerSet;
|
|||||||
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
|
inputStickerSetEmojiDefaultStatuses#29d0f5ee = InputStickerSet;
|
||||||
inputStickerSetEmojiDefaultTopicIcons#44c1f8e9 = InputStickerSet;
|
inputStickerSetEmojiDefaultTopicIcons#44c1f8e9 = InputStickerSet;
|
||||||
inputStickerSetEmojiChannelDefaultStatuses#49748553 = InputStickerSet;
|
inputStickerSetEmojiChannelDefaultStatuses#49748553 = InputStickerSet;
|
||||||
|
inputStickerSetTonGifts#1cf671a0 = InputStickerSet;
|
||||||
|
|
||||||
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true emojis:flags.7?true text_color:flags.9?true channel_emoji_status:flags.10?true creator:flags.11?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
|
stickerSet#2dd14edc flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true emojis:flags.7?true text_color:flags.9?true channel_emoji_status:flags.10?true creator:flags.11?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int thumb_document_id:flags.8?long count:int hash:int = StickerSet;
|
||||||
|
|
||||||
@ -826,7 +831,7 @@ contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector<
|
|||||||
contacts.topPeersDisabled#b52c939d = contacts.TopPeers;
|
contacts.topPeersDisabled#b52c939d = contacts.TopPeers;
|
||||||
|
|
||||||
draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
|
draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
|
||||||
draftMessage#2d65321f flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo message:string entities:flags.3?Vector<MessageEntity> media:flags.5?InputMedia date:int effect:flags.7?long = DraftMessage;
|
draftMessage#96eaa5eb flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo message:string entities:flags.3?Vector<MessageEntity> media:flags.5?InputMedia date:int effect:flags.7?long suggested_post:flags.8?SuggestedPost = DraftMessage;
|
||||||
|
|
||||||
messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers;
|
messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers;
|
||||||
messages.featuredStickers#be382906 flags:# premium:flags.0?true hash:long count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
|
messages.featuredStickers#be382906 flags:# premium:flags.0?true hash:long count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
|
||||||
@ -1206,7 +1211,7 @@ chatOnlines#f041e250 onlines:int = ChatOnlines;
|
|||||||
|
|
||||||
statsURL#47a971e0 url:string = StatsURL;
|
statsURL#47a971e0 url:string = StatsURL;
|
||||||
|
|
||||||
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true manage_topics:flags.13?true post_stories:flags.14?true edit_stories:flags.15?true delete_stories:flags.16?true = ChatAdminRights;
|
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true manage_call:flags.11?true other:flags.12?true manage_topics:flags.13?true post_stories:flags.14?true edit_stories:flags.15?true delete_stories:flags.16?true manage_direct_messages:flags.17?true = ChatAdminRights;
|
||||||
|
|
||||||
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true manage_topics:flags.18?true send_photos:flags.19?true send_videos:flags.20?true send_roundvideos:flags.21?true send_audios:flags.22?true send_voices:flags.23?true send_docs:flags.24?true send_plain:flags.25?true until_date:int = ChatBannedRights;
|
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true manage_topics:flags.18?true send_photos:flags.19?true send_videos:flags.20?true send_roundvideos:flags.21?true send_audios:flags.22?true send_voices:flags.23?true send_docs:flags.24?true send_plain:flags.25?true until_date:int = ChatBannedRights;
|
||||||
|
|
||||||
@ -1824,23 +1829,11 @@ channels.sponsoredMessageReportResultChooseOption#846f9e42 title:string options:
|
|||||||
channels.sponsoredMessageReportResultAdsHidden#3e3bcf2f = channels.SponsoredMessageReportResult;
|
channels.sponsoredMessageReportResultAdsHidden#3e3bcf2f = channels.SponsoredMessageReportResult;
|
||||||
channels.sponsoredMessageReportResultReported#ad798849 = channels.SponsoredMessageReportResult;
|
channels.sponsoredMessageReportResultReported#ad798849 = channels.SponsoredMessageReportResult;
|
||||||
|
|
||||||
stats.broadcastRevenueStats#5407e297 top_hours_graph:StatsGraph revenue_graph:StatsGraph balances:BroadcastRevenueBalances usd_rate:double = stats.BroadcastRevenueStats;
|
|
||||||
|
|
||||||
stats.broadcastRevenueWithdrawalUrl#ec659737 url:string = stats.BroadcastRevenueWithdrawalUrl;
|
|
||||||
|
|
||||||
broadcastRevenueTransactionProceeds#557e2cc4 amount:long from_date:int to_date:int = BroadcastRevenueTransaction;
|
|
||||||
broadcastRevenueTransactionWithdrawal#5a590978 flags:# pending:flags.0?true failed:flags.2?true amount:long date:int provider:string transaction_date:flags.1?int transaction_url:flags.1?string = BroadcastRevenueTransaction;
|
|
||||||
broadcastRevenueTransactionRefund#42d30d2e amount:long date:int provider:string = BroadcastRevenueTransaction;
|
|
||||||
|
|
||||||
stats.broadcastRevenueTransactions#87158466 count:int transactions:Vector<BroadcastRevenueTransaction> = stats.BroadcastRevenueTransactions;
|
|
||||||
|
|
||||||
reactionNotificationsFromContacts#bac3a61a = ReactionNotificationsFrom;
|
reactionNotificationsFromContacts#bac3a61a = ReactionNotificationsFrom;
|
||||||
reactionNotificationsFromAll#4b9e22a0 = ReactionNotificationsFrom;
|
reactionNotificationsFromAll#4b9e22a0 = ReactionNotificationsFrom;
|
||||||
|
|
||||||
reactionsNotifySettings#56e34970 flags:# messages_notify_from:flags.0?ReactionNotificationsFrom stories_notify_from:flags.1?ReactionNotificationsFrom sound:NotificationSound show_previews:Bool = ReactionsNotifySettings;
|
reactionsNotifySettings#56e34970 flags:# messages_notify_from:flags.0?ReactionNotificationsFrom stories_notify_from:flags.1?ReactionNotificationsFrom sound:NotificationSound show_previews:Bool = ReactionsNotifySettings;
|
||||||
|
|
||||||
broadcastRevenueBalances#c3ff71e7 flags:# withdrawal_enabled:flags.0?true current_balance:long available_balance:long overall_revenue:long = BroadcastRevenueBalances;
|
|
||||||
|
|
||||||
availableEffect#93c3e27e flags:# premium_required:flags.2?true id:long emoticon:string static_icon_id:flags.0?long effect_sticker_id:long effect_animation_id:flags.1?long = AvailableEffect;
|
availableEffect#93c3e27e flags:# premium_required:flags.2?true id:long emoticon:string static_icon_id:flags.0?long effect_sticker_id:long effect_animation_id:flags.1?long = AvailableEffect;
|
||||||
|
|
||||||
messages.availableEffectsNotModified#d1ed9a5b = messages.AvailableEffects;
|
messages.availableEffectsNotModified#d1ed9a5b = messages.AvailableEffects;
|
||||||
@ -1859,7 +1852,7 @@ starsTransactionPeerAPI#f9677aad = StarsTransactionPeer;
|
|||||||
|
|
||||||
starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption;
|
starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption;
|
||||||
|
|
||||||
starsTransaction#a39fd94a flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true stargift_upgrade:flags.18?true business_transfer:flags.21?true stargift_resale:flags.22?true id:string stars:StarsAmount date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector<MessageMedia> subscription_period:flags.12?int giveaway_post_id:flags.13?int stargift:flags.14?StarGift floodskip_number:flags.15?int starref_commission_permille:flags.16?int starref_peer:flags.17?Peer starref_amount:flags.17?StarsAmount paid_messages:flags.19?int premium_gift_months:flags.20?int = StarsTransaction;
|
starsTransaction#13659eb0 flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true stargift_upgrade:flags.18?true business_transfer:flags.21?true stargift_resale:flags.22?true id:string amount:StarsAmount date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector<MessageMedia> subscription_period:flags.12?int giveaway_post_id:flags.13?int stargift:flags.14?StarGift floodskip_number:flags.15?int starref_commission_permille:flags.16?int starref_peer:flags.17?Peer starref_amount:flags.17?StarsAmount paid_messages:flags.19?int premium_gift_months:flags.20?int ads_proceeds_from_date:flags.23?int ads_proceeds_to_date:flags.23?int = StarsTransaction;
|
||||||
|
|
||||||
payments.starsStatus#6c9ce8ed flags:# balance:StarsAmount subscriptions:flags.1?Vector<StarsSubscription> subscriptions_next_offset:flags.2?string subscriptions_missing_balance:flags.4?long history:flags.3?Vector<StarsTransaction> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = payments.StarsStatus;
|
payments.starsStatus#6c9ce8ed flags:# balance:StarsAmount subscriptions:flags.1?Vector<StarsSubscription> subscriptions_next_offset:flags.2?string subscriptions_missing_balance:flags.4?long history:flags.3?Vector<StarsTransaction> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = payments.StarsStatus;
|
||||||
|
|
||||||
@ -1871,7 +1864,7 @@ geoPointAddress#de4c5d93 flags:# country_iso2:string state:flags.0?string city:f
|
|||||||
|
|
||||||
starsRevenueStatus#febe5491 flags:# withdrawal_enabled:flags.0?true current_balance:StarsAmount available_balance:StarsAmount overall_revenue:StarsAmount next_withdrawal_at:flags.1?int = StarsRevenueStatus;
|
starsRevenueStatus#febe5491 flags:# withdrawal_enabled:flags.0?true current_balance:StarsAmount available_balance:StarsAmount overall_revenue:StarsAmount next_withdrawal_at:flags.1?int = StarsRevenueStatus;
|
||||||
|
|
||||||
payments.starsRevenueStats#c92bb73b revenue_graph:StatsGraph status:StarsRevenueStatus usd_rate:double = payments.StarsRevenueStats;
|
payments.starsRevenueStats#6c207376 flags:# top_hours_graph:flags.0?StatsGraph revenue_graph:StatsGraph status:StarsRevenueStatus usd_rate:double = payments.StarsRevenueStats;
|
||||||
|
|
||||||
payments.starsRevenueWithdrawalUrl#1dab80b7 url:string = payments.StarsRevenueWithdrawalUrl;
|
payments.starsRevenueWithdrawalUrl#1dab80b7 url:string = payments.StarsRevenueWithdrawalUrl;
|
||||||
|
|
||||||
@ -1897,11 +1890,11 @@ starsGiveawayOption#94ce852a flags:# extended:flags.0?true default:flags.1?true
|
|||||||
|
|
||||||
starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption;
|
starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption;
|
||||||
|
|
||||||
starGift#c62aca28 flags:# limited:flags.0?true sold_out:flags.1?true birthday:flags.2?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int availability_resale:flags.4?long convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int upgrade_stars:flags.3?long resell_min_stars:flags.4?long title:flags.5?string = StarGift;
|
starGift#7f853c12 flags:# limited:flags.0?true sold_out:flags.1?true birthday:flags.2?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int availability_resale:flags.4?long convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int upgrade_stars:flags.3?long resell_min_stars:flags.4?long title:flags.5?string released_by:flags.6?Peer = StarGift;
|
||||||
starGiftUnique#6411db89 flags:# id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int gift_address:flags.3?string resell_stars:flags.4?long = StarGift;
|
starGiftUnique#f63778ae flags:# id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector<StarGiftAttribute> availability_issued:int availability_total:int gift_address:flags.3?string resell_stars:flags.4?long released_by:flags.5?Peer = StarGift;
|
||||||
|
|
||||||
payments.starGiftsNotModified#a388a368 = payments.StarGifts;
|
payments.starGiftsNotModified#a388a368 = payments.StarGifts;
|
||||||
payments.starGifts#901689ea hash:int gifts:Vector<StarGift> = payments.StarGifts;
|
payments.starGifts#2ed82995 hash:int gifts:Vector<StarGift> chats:Vector<Chat> users:Vector<User> = payments.StarGifts;
|
||||||
|
|
||||||
messageReportOption#7903e3d9 text:string option:bytes = MessageReportOption;
|
messageReportOption#7903e3d9 text:string option:bytes = MessageReportOption;
|
||||||
|
|
||||||
@ -1924,6 +1917,7 @@ payments.connectedStarRefBots#98d5ea1d count:int connected_bots:Vector<Connected
|
|||||||
payments.suggestedStarRefBots#b4d5d859 flags:# count:int suggested_bots:Vector<StarRefProgram> users:Vector<User> next_offset:flags.0?string = payments.SuggestedStarRefBots;
|
payments.suggestedStarRefBots#b4d5d859 flags:# count:int suggested_bots:Vector<StarRefProgram> users:Vector<User> next_offset:flags.0?string = payments.SuggestedStarRefBots;
|
||||||
|
|
||||||
starsAmount#bbb6b4a3 amount:long nanos:int = StarsAmount;
|
starsAmount#bbb6b4a3 amount:long nanos:int = StarsAmount;
|
||||||
|
starsTonAmount#74aee3e0 amount:long = StarsAmount;
|
||||||
|
|
||||||
messages.foundStickersNotModified#6010c534 flags:# next_offset:flags.0?int = messages.FoundStickers;
|
messages.foundStickersNotModified#6010c534 flags:# next_offset:flags.0?int = messages.FoundStickers;
|
||||||
messages.foundStickers#82c9e290 flags:# next_offset:flags.0?int hash:long stickers:Vector<Document> = messages.FoundStickers;
|
messages.foundStickers#82c9e290 flags:# next_offset:flags.0?int hash:long stickers:Vector<Document> = messages.FoundStickers;
|
||||||
@ -1993,6 +1987,8 @@ todoList#49b92a26 flags:# others_can_append:flags.0?true others_can_complete:fla
|
|||||||
|
|
||||||
todoCompletion#4cc120b7 id:int completed_by:long date:int = TodoCompletion;
|
todoCompletion#4cc120b7 id:int completed_by:long date:int = TodoCompletion;
|
||||||
|
|
||||||
|
suggestedPost#e8e37e5 flags:# accepted:flags.1?true rejected:flags.2?true price:flags.3?StarsAmount schedule_date:flags.0?int = SuggestedPost;
|
||||||
|
|
||||||
---functions---
|
---functions---
|
||||||
|
|
||||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||||
@ -2004,7 +2000,7 @@ invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
|
|||||||
invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
|
invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
|
||||||
invokeWithBusinessConnection#dd289f8e {X:Type} connection_id:string query:!X = X;
|
invokeWithBusinessConnection#dd289f8e {X:Type} connection_id:string query:!X = X;
|
||||||
invokeWithGooglePlayIntegrity#1df92984 {X:Type} nonce:string token:string query:!X = X;
|
invokeWithGooglePlayIntegrity#1df92984 {X:Type} nonce:string token:string query:!X = X;
|
||||||
invokeWithApnsSecret#0dae54f8 {X:Type} nonce:string secret:string query:!X = X;
|
invokeWithApnsSecret#dae54f8 {X:Type} nonce:string secret:string query:!X = X;
|
||||||
invokeWithReCaptcha#adbb0f94 {X:Type} token:string query:!X = X;
|
invokeWithReCaptcha#adbb0f94 {X:Type} token:string query:!X = X;
|
||||||
|
|
||||||
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
|
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
|
||||||
@ -2189,9 +2185,9 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t
|
|||||||
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
|
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
|
||||||
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
||||||
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
|
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
|
||||||
messages.sendMessage#fbf2340a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates;
|
messages.sendMessage#fe05dc9a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long suggested_post:flags.22?SuggestedPost = Updates;
|
||||||
messages.sendMedia#a550cd78 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long = Updates;
|
messages.sendMedia#ac55d9c1 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true allow_paid_floodskip:flags.19?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long allow_paid_stars:flags.21?long suggested_post:flags.22?SuggestedPost = Updates;
|
||||||
messages.forwardMessages#38f0188c flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true allow_paid_floodskip:flags.19?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int reply_to:flags.22?InputReplyTo schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut video_timestamp:flags.20?int allow_paid_stars:flags.21?long = Updates;
|
messages.forwardMessages#978928ca flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true allow_paid_floodskip:flags.19?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer top_msg_id:flags.9?int reply_to:flags.22?InputReplyTo schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut video_timestamp:flags.20?int allow_paid_stars:flags.21?long suggested_post:flags.23?SuggestedPost = Updates;
|
||||||
messages.reportSpam#cf1592db peer:InputPeer = Bool;
|
messages.reportSpam#cf1592db peer:InputPeer = Bool;
|
||||||
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
|
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
|
||||||
messages.report#fc78af9b peer:InputPeer id:Vector<int> option:bytes message:string = ReportResult;
|
messages.report#fc78af9b peer:InputPeer id:Vector<int> option:bytes message:string = ReportResult;
|
||||||
@ -2241,7 +2237,7 @@ messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true invert_me
|
|||||||
messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
|
messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
|
||||||
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
|
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
|
||||||
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
|
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
|
||||||
messages.saveDraft#d372c5ce flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> media:flags.5?InputMedia effect:flags.7?long = Bool;
|
messages.saveDraft#54ae308e flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> media:flags.5?InputMedia effect:flags.7?long suggested_post:flags.8?SuggestedPost = Bool;
|
||||||
messages.getAllDrafts#6a3f8d65 = Updates;
|
messages.getAllDrafts#6a3f8d65 = Updates;
|
||||||
messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers;
|
messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers;
|
||||||
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
|
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
|
||||||
@ -2409,6 +2405,7 @@ messages.getSavedDialogsByID#6f6f9c96 flags:# parent_peer:flags.1?InputPeer ids:
|
|||||||
messages.readSavedHistory#ba4a3b5b parent_peer:InputPeer peer:InputPeer max_id:int = Bool;
|
messages.readSavedHistory#ba4a3b5b parent_peer:InputPeer peer:InputPeer max_id:int = Bool;
|
||||||
messages.toggleTodoCompleted#d3e03124 peer:InputPeer msg_id:int completed:Vector<int> incompleted:Vector<int> = Updates;
|
messages.toggleTodoCompleted#d3e03124 peer:InputPeer msg_id:int completed:Vector<int> incompleted:Vector<int> = Updates;
|
||||||
messages.appendTodoList#21a61057 peer:InputPeer msg_id:int list:Vector<TodoItem> = Updates;
|
messages.appendTodoList#21a61057 peer:InputPeer msg_id:int list:Vector<TodoItem> = Updates;
|
||||||
|
messages.toggleSuggestedPostApproval#8107455c flags:# reject:flags.1?true peer:InputPeer msg_id:int schedule_date:flags.0?int reject_comment:flags.2?string = Updates;
|
||||||
|
|
||||||
updates.getState#edd4882a = updates.State;
|
updates.getState#edd4882a = updates.State;
|
||||||
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
|
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
|
||||||
@ -2567,14 +2564,14 @@ payments.applyGiftCode#f6e26854 slug:string = Updates;
|
|||||||
payments.getGiveawayInfo#f4239425 peer:InputPeer msg_id:int = payments.GiveawayInfo;
|
payments.getGiveawayInfo#f4239425 peer:InputPeer msg_id:int = payments.GiveawayInfo;
|
||||||
payments.launchPrepaidGiveaway#5ff58f20 peer:InputPeer giveaway_id:long purpose:InputStorePaymentPurpose = Updates;
|
payments.launchPrepaidGiveaway#5ff58f20 peer:InputPeer giveaway_id:long purpose:InputStorePaymentPurpose = Updates;
|
||||||
payments.getStarsTopupOptions#c00ec7d3 = Vector<StarsTopupOption>;
|
payments.getStarsTopupOptions#c00ec7d3 = Vector<StarsTopupOption>;
|
||||||
payments.getStarsStatus#104fcfa7 peer:InputPeer = payments.StarsStatus;
|
payments.getStarsStatus#4ea9b3bf flags:# ton:flags.0?true peer:InputPeer = payments.StarsStatus;
|
||||||
payments.getStarsTransactions#69da4557 flags:# inbound:flags.0?true outbound:flags.1?true ascending:flags.2?true subscription_id:flags.3?string peer:InputPeer offset:string limit:int = payments.StarsStatus;
|
payments.getStarsTransactions#69da4557 flags:# inbound:flags.0?true outbound:flags.1?true ascending:flags.2?true ton:flags.4?true subscription_id:flags.3?string peer:InputPeer offset:string limit:int = payments.StarsStatus;
|
||||||
payments.sendStarsForm#7998c914 form_id:long invoice:InputInvoice = payments.PaymentResult;
|
payments.sendStarsForm#7998c914 form_id:long invoice:InputInvoice = payments.PaymentResult;
|
||||||
payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates;
|
payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates;
|
||||||
payments.getStarsRevenueStats#d91ffad6 flags:# dark:flags.0?true peer:InputPeer = payments.StarsRevenueStats;
|
payments.getStarsRevenueStats#d91ffad6 flags:# dark:flags.0?true ton:flags.1?true peer:InputPeer = payments.StarsRevenueStats;
|
||||||
payments.getStarsRevenueWithdrawalUrl#13bbe8b3 peer:InputPeer stars:long password:InputCheckPasswordSRP = payments.StarsRevenueWithdrawalUrl;
|
payments.getStarsRevenueWithdrawalUrl#2433dc92 flags:# ton:flags.0?true peer:InputPeer amount:flags.1?long password:InputCheckPasswordSRP = payments.StarsRevenueWithdrawalUrl;
|
||||||
payments.getStarsRevenueAdsAccountUrl#d1d7efc5 peer:InputPeer = payments.StarsRevenueAdsAccountUrl;
|
payments.getStarsRevenueAdsAccountUrl#d1d7efc5 peer:InputPeer = payments.StarsRevenueAdsAccountUrl;
|
||||||
payments.getStarsTransactionsByID#27842d2e peer:InputPeer id:Vector<InputStarsTransaction> = payments.StarsStatus;
|
payments.getStarsTransactionsByID#2dca16b8 flags:# ton:flags.0?true peer:InputPeer id:Vector<InputStarsTransaction> = payments.StarsStatus;
|
||||||
payments.getStarsGiftOptions#d3c96bc8 flags:# user_id:flags.0?InputUser = Vector<StarsGiftOption>;
|
payments.getStarsGiftOptions#d3c96bc8 flags:# user_id:flags.0?InputUser = Vector<StarsGiftOption>;
|
||||||
payments.getStarsSubscriptions#32512c5 flags:# missing_balance:flags.0?true peer:InputPeer offset:string = payments.StarsStatus;
|
payments.getStarsSubscriptions#32512c5 flags:# missing_balance:flags.0?true peer:InputPeer offset:string = payments.StarsStatus;
|
||||||
payments.changeStarsSubscription#c7770878 flags:# peer:InputPeer subscription_id:string canceled:flags.0?Bool = Bool;
|
payments.changeStarsSubscription#c7770878 flags:# peer:InputPeer subscription_id:string canceled:flags.0?Bool = Bool;
|
||||||
@ -2667,9 +2664,6 @@ stats.getMessagePublicForwards#5f150144 channel:InputChannel msg_id:int offset:s
|
|||||||
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
|
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
|
||||||
stats.getStoryStats#374fef40 flags:# dark:flags.0?true peer:InputPeer id:int = stats.StoryStats;
|
stats.getStoryStats#374fef40 flags:# dark:flags.0?true peer:InputPeer id:int = stats.StoryStats;
|
||||||
stats.getStoryPublicForwards#a6437ef6 peer:InputPeer id:int offset:string limit:int = stats.PublicForwards;
|
stats.getStoryPublicForwards#a6437ef6 peer:InputPeer id:int offset:string limit:int = stats.PublicForwards;
|
||||||
stats.getBroadcastRevenueStats#f788ee19 flags:# dark:flags.0?true peer:InputPeer = stats.BroadcastRevenueStats;
|
|
||||||
stats.getBroadcastRevenueWithdrawalUrl#9df4faad peer:InputPeer password:InputCheckPasswordSRP = stats.BroadcastRevenueWithdrawalUrl;
|
|
||||||
stats.getBroadcastRevenueTransactions#70990b6d peer:InputPeer offset:int limit:int = stats.BroadcastRevenueTransactions;
|
|
||||||
|
|
||||||
chatlists.exportChatlistInvite#8472478e chatlist:InputChatlist title:string peers:Vector<InputPeer> = chatlists.ExportedChatlistInvite;
|
chatlists.exportChatlistInvite#8472478e chatlist:InputChatlist title:string peers:Vector<InputPeer> = chatlists.ExportedChatlistInvite;
|
||||||
chatlists.deleteExportedInvite#719c5c5e chatlist:InputChatlist slug:string = Bool;
|
chatlists.deleteExportedInvite#719c5c5e chatlist:InputChatlist slug:string = Bool;
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import type {
|
|||||||
ApiFakeType,
|
ApiFakeType,
|
||||||
ApiFormattedText,
|
ApiFormattedText,
|
||||||
ApiInputReplyInfo,
|
ApiInputReplyInfo,
|
||||||
|
ApiInputSuggestedPostInfo,
|
||||||
ApiLabeledPrice,
|
ApiLabeledPrice,
|
||||||
ApiLanguage,
|
ApiLanguage,
|
||||||
ApiMediaFormat,
|
ApiMediaFormat,
|
||||||
@ -40,6 +41,7 @@ import type {
|
|||||||
ApiTopic,
|
ApiTopic,
|
||||||
ApiTypingStatus,
|
ApiTypingStatus,
|
||||||
ApiVideo,
|
ApiVideo,
|
||||||
|
MediaContent,
|
||||||
StarGiftAttributeIdModel,
|
StarGiftAttributeIdModel,
|
||||||
} from '../api/types';
|
} from '../api/types';
|
||||||
import type { DC_IDS } from '../config';
|
import type { DC_IDS } from '../config';
|
||||||
@ -726,6 +728,7 @@ export type SendMessageParams = {
|
|||||||
text?: string;
|
text?: string;
|
||||||
entities?: ApiMessageEntity[];
|
entities?: ApiMessageEntity[];
|
||||||
replyInfo?: ApiInputReplyInfo;
|
replyInfo?: ApiInputReplyInfo;
|
||||||
|
suggestedPostInfo?: ApiInputSuggestedPostInfo;
|
||||||
attachment?: ApiAttachment;
|
attachment?: ApiAttachment;
|
||||||
sticker?: ApiSticker;
|
sticker?: ApiSticker;
|
||||||
story?: ApiStory | ApiStorySkipped;
|
story?: ApiStory | ApiStorySkipped;
|
||||||
@ -755,6 +758,7 @@ export type SendMessageParams = {
|
|||||||
isForwarding?: boolean;
|
isForwarding?: boolean;
|
||||||
forwardParams?: ForwardMessagesParams;
|
forwardParams?: ForwardMessagesParams;
|
||||||
isStoryReply?: boolean;
|
isStoryReply?: boolean;
|
||||||
|
suggestedMedia?: MediaContent;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ForwardedLocalMessagesSlice = {
|
export type ForwardedLocalMessagesSlice = {
|
||||||
|
|||||||
162
src/types/language.d.ts
vendored
162
src/types/language.d.ts
vendored
@ -1538,6 +1538,38 @@ export interface LangPair {
|
|||||||
'MonoforumComposerPlaceholder': undefined;
|
'MonoforumComposerPlaceholder': undefined;
|
||||||
'ChannelSendMessage': undefined;
|
'ChannelSendMessage': undefined;
|
||||||
'AutomaticTranslation': undefined;
|
'AutomaticTranslation': undefined;
|
||||||
|
'ComposerEmbeddedMessageSuggestedPostTitle': undefined;
|
||||||
|
'ComposerEmbeddedMessageSuggestedPostDescription': undefined;
|
||||||
|
'ActionSuggestedPostOutgoing': undefined;
|
||||||
|
'ActionSuggestedChangesPrice': undefined;
|
||||||
|
'ActionSuggestedChangesText': undefined;
|
||||||
|
'ActionSuggestedChangesTime': undefined;
|
||||||
|
'ActionSuggestedChangesMedia': undefined;
|
||||||
|
'TitlePrice': undefined;
|
||||||
|
'TitleTime': undefined;
|
||||||
|
'TitleSuggestMessage': undefined;
|
||||||
|
'TitleSuggestedChanges': undefined;
|
||||||
|
'EnterPriceInStars': undefined;
|
||||||
|
'SuggestMessageDateTimeHint': undefined;
|
||||||
|
'TitleAnytime': undefined;
|
||||||
|
'ButtonOfferFree': undefined;
|
||||||
|
'ButtonUpdateTerms': undefined;
|
||||||
|
'InputPlaceholderPrice': undefined;
|
||||||
|
'SuggestedPostApprove': undefined;
|
||||||
|
'SuggestedPostDecline': undefined;
|
||||||
|
'SuggestedPostSuggestChanges': undefined;
|
||||||
|
'InputTitleSuggestMessageTime': undefined;
|
||||||
|
'SuggestedPostApproved': undefined;
|
||||||
|
'SuggestedPostRejectedNotification': undefined;
|
||||||
|
'SuggestedPostAgreementReached': undefined;
|
||||||
|
'CurrencyStars': undefined;
|
||||||
|
'DeclineReasonPlaceholder': undefined;
|
||||||
|
'SuggestedPostRejectedYou': undefined;
|
||||||
|
'SuggestedPostRejectedWithReasonYou': undefined;
|
||||||
|
'ComposerPlaceholderCaption': undefined;
|
||||||
|
'SuggestedPostConfirmTitle': undefined;
|
||||||
|
'ButtonPublish': undefined;
|
||||||
|
'PublishNow': undefined;
|
||||||
'TitleNewToDoList': undefined;
|
'TitleNewToDoList': undefined;
|
||||||
'TitleEditToDoList': undefined;
|
'TitleEditToDoList': undefined;
|
||||||
'TitleAppendToDoList': undefined;
|
'TitleAppendToDoList': undefined;
|
||||||
@ -2539,6 +2571,132 @@ export interface LangPairWithVariables<V = LangVariable> {
|
|||||||
'ComposerTitleForwardFrom': {
|
'ComposerTitleForwardFrom': {
|
||||||
'users': V;
|
'users': V;
|
||||||
};
|
};
|
||||||
|
'TitleSuggestedPostAmountForAnyTime': {
|
||||||
|
'amount': V;
|
||||||
|
};
|
||||||
|
'ActionSuggestedPostIncoming': {
|
||||||
|
'user': V;
|
||||||
|
};
|
||||||
|
'ActionSuggestedChangesOutgoing': {
|
||||||
|
'changes': V;
|
||||||
|
};
|
||||||
|
'ActionSuggestedChangesIncoming': {
|
||||||
|
'user': V;
|
||||||
|
'changes': V;
|
||||||
|
};
|
||||||
|
'SuggestMessagePriceDescription': {
|
||||||
|
'currency': V;
|
||||||
|
};
|
||||||
|
'SuggestMessageTimeDescription': {
|
||||||
|
'hint': V;
|
||||||
|
'duration': V;
|
||||||
|
};
|
||||||
|
'ButtonOfferAmount': {
|
||||||
|
'amount': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostPublishSchedule': {
|
||||||
|
'peer': V;
|
||||||
|
'date': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostPublishScheduleYou': {
|
||||||
|
'peer': V;
|
||||||
|
'date': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostPublished': {
|
||||||
|
'peer': V;
|
||||||
|
'date': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostPublishedYou': {
|
||||||
|
'peer': V;
|
||||||
|
'date': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostCharged': {
|
||||||
|
'user': V;
|
||||||
|
'amount': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostChargedYou': {
|
||||||
|
'amount': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostReceiveAmount': {
|
||||||
|
'peer': V;
|
||||||
|
'currency': V;
|
||||||
|
'duration': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostReceiveAmountYou': {
|
||||||
|
'peer': V;
|
||||||
|
'currency': V;
|
||||||
|
'duration': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostRefund': {
|
||||||
|
'peer': V;
|
||||||
|
'duration': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostRefundYou': {
|
||||||
|
'peer': V;
|
||||||
|
'duration': V;
|
||||||
|
'currency': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostBalanceTooLow': {
|
||||||
|
'peer': V;
|
||||||
|
'currency': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostRefundedByUser': {
|
||||||
|
'channel': V;
|
||||||
|
'amount': V;
|
||||||
|
'user': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostRefundedByChannel': {
|
||||||
|
'amount': V;
|
||||||
|
'peer': V;
|
||||||
|
'channel': V;
|
||||||
|
};
|
||||||
|
'DeclinePostDialogQuestion': {
|
||||||
|
'sender': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostRejected': {
|
||||||
|
'peer': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostRejectedWithReason': {
|
||||||
|
'peer': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostRejectedComment': {
|
||||||
|
'comment': V;
|
||||||
|
};
|
||||||
|
'ActionSuggestedPostSuccess': {
|
||||||
|
'channel': V;
|
||||||
|
'amount': V;
|
||||||
|
};
|
||||||
|
'DescriptionSuggestedPostMinimumOffer': {
|
||||||
|
'amount': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostConfirmMessage': {
|
||||||
|
'peer': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostConfirmDetailsAdmin': {
|
||||||
|
'amount': V;
|
||||||
|
'commission': V;
|
||||||
|
'duration': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostConfirmDetailsUser': {
|
||||||
|
'amount': V;
|
||||||
|
'commission': V;
|
||||||
|
'duration': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostConfirmDetailsWithTimeAdmin': {
|
||||||
|
'amount': V;
|
||||||
|
'commission': V;
|
||||||
|
'time': V;
|
||||||
|
'duration': V;
|
||||||
|
};
|
||||||
|
'SuggestedPostConfirmDetailsWithTimeUser': {
|
||||||
|
'amount': V;
|
||||||
|
'commission': V;
|
||||||
|
'time': V;
|
||||||
|
'duration': V;
|
||||||
|
};
|
||||||
|
'ButtonPublishAtTime': {
|
||||||
|
'time': V;
|
||||||
|
};
|
||||||
'TitleUserToDoList': {
|
'TitleUserToDoList': {
|
||||||
'peer': V;
|
'peer': V;
|
||||||
};
|
};
|
||||||
@ -2595,6 +2753,10 @@ export interface LangPairWithVariables<V = LangVariable> {
|
|||||||
'HintTodoListTasksCount': {
|
'HintTodoListTasksCount': {
|
||||||
'count': V;
|
'count': V;
|
||||||
};
|
};
|
||||||
|
'GiftInfoCollectibleBy': {
|
||||||
|
'number': V;
|
||||||
|
'owner': V;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LangPairPlural {
|
export interface LangPairPlural {
|
||||||
|
|||||||
@ -227,6 +227,21 @@ export function formatTimeDuration(lang: OldLangFn, duration: number, showLast =
|
|||||||
return out.map((part) => lang(part.type, part.duration, 'i')).join(', ');
|
return out.map((part) => lang(part.type, part.duration, 'i')).join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatScheduledDateTime(
|
||||||
|
scheduleDateTimestamp: number,
|
||||||
|
lang: LangFn,
|
||||||
|
oldLang: OldLangFn,
|
||||||
|
): string {
|
||||||
|
const scheduleDate = new Date(scheduleDateTimestamp * 1000);
|
||||||
|
|
||||||
|
return lang('FormatDateAtTime', {
|
||||||
|
date: isToday(scheduleDate)
|
||||||
|
? lang('WeekdayToday')
|
||||||
|
: formatHumanDate(oldLang, scheduleDateTimestamp * 1000, true, false, true),
|
||||||
|
time: formatTime(oldLang, scheduleDateTimestamp * 1000),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function formatHumanDate(
|
export function formatHumanDate(
|
||||||
lang: OldLangFn,
|
lang: OldLangFn,
|
||||||
datetime: number | Date,
|
datetime: number | Date,
|
||||||
@ -407,7 +422,7 @@ export function formatDateAtTime(
|
|||||||
return lang('formatDateAtTime', [formattedDate, time]);
|
return lang('formatDateAtTime', [formattedDate, time]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatShortDuration(lang: LangFn, duration: number) {
|
export function formatShortDuration(lang: LangFn, duration: number, hoursPriority?: boolean) {
|
||||||
if (duration < 0) {
|
if (duration < 0) {
|
||||||
return lang('RightNow');
|
return lang('RightNow');
|
||||||
}
|
}
|
||||||
@ -422,7 +437,7 @@ export function formatShortDuration(lang: LangFn, duration: number) {
|
|||||||
return lang('Minutes', { count }, { pluralValue: count });
|
return lang('Minutes', { count }, { pluralValue: count });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (duration < 60 * 60 * 24) {
|
if (duration < 60 * 60 * 24 || (hoursPriority && duration <= 60 * 60 * 24 * 2)) {
|
||||||
const count = Math.ceil(duration / (60 * 60));
|
const count = Math.ceil(duration / (60 * 60));
|
||||||
return lang('Hours', { count }, { pluralValue: count });
|
return lang('Hours', { count }, { pluralValue: count });
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user