Compare commits

...

227 Commits

Author SHA1 Message Date
John Preston
9194c68bd3 Version 1.1.17.
- Bug fixes and other minor improvements.
2017-07-26 23:47:17 +03:00
John Preston
2dadc9bd7a Langs updated. 2017-07-26 23:46:16 +03:00
John Preston
180f83c528 Fix crash in InnerDropdown widget animations. 2017-07-26 23:43:18 +03:00
John Preston
dfa7cb1826 Version 1.1.16: Fix build for Qt 5.3.2 and GCC. 2017-07-26 19:10:04 +03:00
John Preston
fdcb28f1a0 Version 1.1.16: Hide emoji replaces list for now. 2017-07-26 17:59:08 +03:00
John Preston
0e059ec788 Version 1.1.16: Add some debug info. 2017-07-26 17:56:01 +03:00
John Preston
c681f75ad3 Version 1.1.16.
- Autocompletion for emoji. Start typing :e to get suggestions.
- Fixed a bug with forwarding messages.
2017-07-26 15:56:32 +03:00
John Preston
9bf40c927e Improve emoji suggestions and replace on send.
Bring recent emoji up in suggestions.
Apply saved skin color to emoji suggestions.
Write to recents when choosing emoji suggestion.
2017-07-26 15:55:04 +03:00
John Preston
df2aeb0c58 Provide emoji suggestions as a service. 2017-07-26 15:55:03 +03:00
John Preston
2dec9c46a7 Add emoji autocomplete to history message field. 2017-07-26 15:55:02 +03:00
John Preston
8f8100af3a Add emoji autocomplete data and algorithm. 2017-07-26 15:55:01 +03:00
John Preston
65371ec1b8 Fix selected text render on Retina.
QPainter::viewport() can be a too large rect to be passed as a
clipping region to QPainter on Retina displays.

QPainter in case of Retina displays tries to transform QRegion using
scale matrix and fails with "QRegion: creating region from big
polygon failed" warning message and disables painting completely.
2017-07-26 15:51:24 +03:00
John Preston
647d6ae443 Fix language switch when getting difference. 2017-07-26 15:50:39 +03:00
John Preston
216692cd9b Remove the Link row in Settings.
Anyway it duplicates the Username row.
2017-07-26 14:56:58 +03:00
John Preston
2e5930eb58 Update full peer in profile and settings.
Not more than once in five seconds get the full info to update bio.
2017-07-26 14:53:49 +03:00
John Preston
24b3b2a658 Fix forwarded from for single message forward.
Closes #3699, closes #3701, closes #3700.
2017-07-26 09:07:05 +03:00
John Preston
e1e286aa13 Allow to fast share not games to channels.
Fixes #3692.
2017-07-26 09:07:01 +03:00
John Preston
4081ceff22 Version 1.1.15.
— Send **bold** and __italic__ text in your messages.
— Get a share link for posts in public supergroups.
— Quickly share posts from channels and media messages from bots.
— Search large supergroup members by name.
— Search channel members by name for admins.
— Use search in the service actions log.
— Ban supergroup members via the right click menu in the service actions log.
2017-07-23 15:39:32 +03:00
John Preston
7db57b72f1 Some langs updated. 2017-07-23 15:37:44 +03:00
John Preston
9e0ca64e02 Closed beta 1001014001. 2017-07-23 13:30:02 +03:00
John Preston
cc55e3b027 Don't jump in chats list scroll while reordering.
While we reorder the pinned chats we move chat rows around which
sometimes produces auto scroll event that will try to reorder more.

This should probably fix assertion violation that was debugged using
additional information from commits 8b96f72, c7e63ff and a935075.
2017-07-23 13:20:11 +03:00
John Preston
f78335aa67 Fix keyboard scrolling in ContactsBox. 2017-07-23 12:48:49 +03:00
John Preston
f283aa033b Fix a couple of layout bugs for username edit. 2017-07-23 12:34:44 +03:00
John Preston
7ff175a81e Display user Bio in profile. 2017-07-23 12:34:43 +03:00
John Preston
34d2e78308 Allow to edit Bio in Settings. 2017-07-23 12:34:43 +03:00
John Preston
9bd89121e8 Replace self-destruct media service messages text.
Also support runtime components with align up to std::max_align_t.
2017-07-23 12:34:42 +03:00
John Preston
2e0513a30f Check CDN file hashes. 2017-07-23 12:34:41 +03:00
John Preston
cf02a4cc31 API scheme updated to layer 70. 2017-07-23 12:34:40 +03:00
John Preston
5a1ec3c9e0 Fix display of Search members button in profile. 2017-07-23 12:33:39 +03:00
John Preston
fa70bf9e0c Fix text message edit admin event log entries.
Regression was introduced in a513b1c.

In case of text messages entry->_page minimal height is counted in
the whole message minimal height and should not be added.
2017-07-23 12:33:39 +03:00
John Preston
9bd983bb9d Alpha 1.1.14: Update langs. 2017-07-19 11:38:59 +03:00
John Preston
1618daaeae Alpha 1.1.14.
- Bug fixes and other minor improvements.
2017-07-19 11:35:37 +03:00
John Preston
ed4c3cccb2 Add admins from search in admins list box.
When searching in admins list box show all group members and allow
to appoint a new administrator right from this box.
2017-07-19 11:18:20 +03:00
John Preston
90311dbf24 Fix CDN file download. 2017-07-18 22:01:22 +03:00
John Preston
f7a354bfd1 Display "edited" mark on channel signed messages. 2017-07-18 22:00:33 +03:00
John Preston
a513b1c7b3 Fix media caption edit admin event log entries. 2017-07-18 21:59:53 +03:00
John Preston
077e128e9a Don't uncheck a radiobutton on second click. 2017-07-18 20:11:44 +03:00
John Preston
794ad7bda7 Fix main menu night theme layout for non-English.
The place for the phrase was too small because of huge left padding.
2017-07-18 20:10:55 +03:00
John Preston
10906e2e16 Fix crash in change language box.
Each time a radiobutton was triggered it was destroyed in refresh().
2017-07-18 20:09:48 +03:00
John Preston
a5e0b19b79 Copy correct text for a forwarded reply message. 2017-07-18 19:48:10 +03:00
John Preston
7c6bb132ce Don't display reply preview if it doesn't fit. 2017-07-18 19:47:56 +03:00
John Preston
3ba210b6e4 Automatically return window to screen.
Fixes #3551.
2017-07-18 19:38:11 +03:00
John Preston
faf090781d No confirmation for known hosts links. 2017-07-15 15:41:15 +03:00
John Preston
ab62b1591f Fix duplicated entries in calls log box.
Notify about new call service messages only for unread messages.
2017-07-15 15:41:04 +03:00
John Preston
397e1dde78 Move reply bar to the top of the sticker.
That way a fast share button in channels isn't overlapping with it.
2017-07-15 15:05:46 +03:00
John Preston
a935075782 Add some more assertions and info for crash debug.
An assertion violation happens some time so add some debug info.
2017-07-15 14:22:17 +03:00
John Preston
588aabc6d7 Fix render bug in pinned chats reordering.
If the last chats row was painted a wrong lastPaintedPos was used.
2017-07-15 14:20:53 +03:00
John Preston
ca47e4ebfd Alpha 1.1.13: Fix build for Xcode. 2017-07-14 15:52:53 +03:00
John Preston
58b79ab112 Alpha 1.1.13.
- Various bug fixes.
2017-07-14 15:31:22 +03:00
John Preston
cabf35f2b3 Allow to create channel invite link in boxes.
SetupChannelBox (public/private) and MaxInviteBox are suggesting
to copy the channel invite link. Now they suggest to create it
in case the channel didn't have the invite link already.
2017-07-14 15:28:08 +03:00
John Preston
949104d879 Improve queued by pts updates handling.
The updates are ordered by pts and applied in the correct order.
Also some pts-dependent updates handling was moved to ApiWrap.
2017-07-14 14:57:05 +03:00
John Preston
101ec9a1c1 Remove duplicated Copy Link context menu items.
When right clicking on a real link don't add a message link copy.
Fixes #3662
2017-07-14 14:56:57 +03:00
John Preston
7cc38f8f6a Improve disabled Ui::Checkbox layout. 2017-07-14 12:47:44 +03:00
John Preston
9e73e22e13 Add channel members in ParticipantsBoxController.
Also remove MembersBox, it was replaced completely.
2017-07-14 09:46:57 +03:00
John Preston
4c6e1b2b1f Fix members kick from group members dropdown. 2017-07-13 19:25:28 +03:00
John Preston
3db913f15b Remove layer deletion by timer in MainWindow.
It could be: schedule deletion -> show new box -> delete it by timer.
This fixes the error box display when adding users to groups.
2017-07-13 19:14:31 +03:00
John Preston
bd1547cd5e Show error when trying to delete a large channel.
Server doesn't allow to delete a channel with more than 1000 users.
2017-07-13 19:12:20 +03:00
John Preston
c7e63ffef5 Add some more assertions and info for crash debug.
An assertion violation happens some time so add some debug info.
2017-07-13 18:11:26 +03:00
John Preston
a69304fd25 Fix crash in forwarding messages.
HistoryHider holds pointers to items, so it should watch for
the history items being removed and remove the pointers.
2017-07-13 17:57:25 +03:00
John Preston
e0c74c9546 Fix crash in handlePendingHistoryUpdate().
This method is called from InvokeQueued, so the auth session could
be destroyed already and should be checked before being accessed.
2017-07-13 17:44:45 +03:00
John Preston
2f816942b8 Use objects instead of pointers for corners.
Also don't change mask corner images when color theme is changed.
This prevents race condition in mask corner images access, because
the GIF frame readers access mask corner images from other threads.
2017-07-13 17:42:46 +03:00
John Preston
9fd8b040b7 Fix crash in history switch.
Don't call anything heavy between changing _history and _list.
2017-07-13 17:42:28 +03:00
John Preston
f5a405e6f1 Fix auto media download settings box layout. 2017-07-12 16:53:42 +03:00
John Preston
c79561e97f Add a couple of actions log phrases for channels. 2017-07-12 16:34:13 +03:00
Nicholas Guriev
38a53687a0 Fix a couple of typos
Signed-off-by: Nicholas Guriev <guriev-ns@ya.ru> (github: mymedia2)
2017-07-12 14:08:36 +03:00
Viktor Oreshkin
94e43f8f8a Fix openssl ldflags on macOS
If ld finds ssl/crypto dylibs it prefers them to static archived objects
And because of that produced .app links dynamically to openssl, so it
won't launch unless users install openssl.
There are two solutions: either uninstall openssl completely from build
machine on each build, or use paths to .a files.
Obviously latter is preferred.

Signed-off-by: Viktor Oreshkin <imselfish@stek29.rocks>
2017-07-12 14:06:40 +03:00
John Preston
7b4393ba48 Fix radiobutton list boxes dimensions.
Closes #3655
2017-07-12 14:05:29 +03:00
John Preston
56cb5ac9c6 Alpha 1.1.12: Search in supergroup for count > 50.
Also move search in supergroup members to Actions profile block.
2017-07-11 21:40:51 +03:00
John Preston
76cafc5059 Alpha 1.1.12.
- Click on forwarded messages bar to change the recipient chat
in case you chose a wrong one first.
- Quickly share posts from channels and media messages from bots.
- Search in supergroup members by name.
- Search in channel members by name if you're a channel admin.
- Copy links to messages in public supergroups.
2017-07-11 20:28:37 +03:00
John Preston
48bd693679 Allow to copy message links in public supergroups. 2017-07-11 20:21:24 +03:00
John Preston
f32af6999b Add a fast share button in channels and bots. 2017-07-11 20:11:06 +03:00
John Preston
ac99784bf7 Change chat for forwarded items by click on them.
Closes #3637
2017-07-11 13:23:15 +03:00
John Preston
d94ef82327 Fix opening a forwarded GIF in MediaView on click.
Fixes #3645
2017-07-11 13:21:01 +03:00
John Preston
06d4ea2975 Use ParticipantsBoxController for members list.
Add search in channel/supergroup members inside PeerListBox.
Also MembersBox is not used anymore.
2017-07-11 12:17:27 +03:00
John Preston
b79ddb7a1c Handle click on userpic in edit admin/banned box.
In admins/banned/restricted lists on row click the edit box is shown.
Now click on the userpic in this box allows to get to user profile.
2017-07-10 15:43:30 +03:00
John Preston
58a592ba47 Allow to see admin / banned rights for everyone.
Even if you can't edit admin / banned rights you can see them.
2017-07-10 15:29:55 +03:00
John Preston
9344504781 Show error when adding a non-group bot to admins. 2017-07-10 13:56:29 +03:00
John Preston
f7543e3261 Improve EditAdmin/RestrictedBox design. 2017-07-10 13:55:16 +03:00
John Preston
0402b4f5f4 Add X / V vector icon to checkbox Toggle view.
Also use them in the admin rights / restrictions boxes.
2017-07-09 18:06:27 +03:00
John Preston
d5c5549c1a Make Checkbox text position relative to the View. 2017-07-07 14:27:13 +03:00
John Preston
21d2f6a44f Generalize Checkbox layout.
Now any Checkbox can have Check, Radio or Toggle layout.
Radiobutton is now a subclass of Checkbox with default Radio layout.
2017-07-07 14:16:37 +03:00
John Preston
0ecef54e2b Alpha 1.1.11: Update langs and libtgvoip. 2017-07-06 20:40:49 +03:00
John Preston
f6eccf85dc Alpha 1.1.11.
- Send **bold** and __italic__ text in your messages (in addition
to already supported `monospace` and ```multiline monospace```).
- Search in channel and supergroup admin event log.
- Ban members from right click menu in supergroup admin event log.
2017-07-06 20:11:25 +03:00
John Preston
28f7dc73a5 Optimize markdown parser, use less regex matches. 2017-07-06 19:57:19 +03:00
John Preston
8d43bdb084 Use std::unique_ptrs for ITextBlocks. 2017-07-06 18:59:47 +03:00
John Preston
8550099110 Improve markdown parsing. 2017-07-06 18:59:38 +03:00
John Preston
921c27c9b1 Apply markdown bold/italic when editing a message. 2017-07-06 18:19:50 +03:00
John Preston
148c04fb41 Fix multiline monospace pre blocks editing.
Now we pass current flags and lnkIndex to the NewlineBlock, so that
we don't get one multiline pre block split to separate line blocks.
2017-07-06 17:10:33 +03:00
John Preston
eaf91bba58 Parse bold / italic markdown entities. 2017-07-06 16:44:11 +03:00
John Preston
da0d78135d Some refactoring in working with text entities.
Also move this code to TextUtilities namespace.
2017-07-06 14:37:42 +03:00
John Preston
f38fad2f92 Correctly apply edit admin in channel.
For example update adminCount / membersCount. For that we pass not
only the newRights values, but also oldRights values to applyEdit*().
2017-07-06 00:11:49 +03:00
John Preston
73e550f432 Update libtgvoip. 2017-07-05 22:51:38 +03:00
John Preston
8b96f7214e Add some assertions and info for crash debug.
An assertion violation happens some time so add some debug info.
2017-07-05 22:41:50 +03:00
John Preston
8202a1633b Fix crash in single instance forcing.
Local server communication is allowed before Messenger constructor.
2017-07-05 21:45:46 +03:00
John Preston
7013a0dfef Update channel admin counter after removing admin. 2017-07-05 21:36:09 +03:00
John Preston
0c43aabfec Allow to ban from actions log user context menu. 2017-07-05 21:11:31 +03:00
John Preston
1a7353fb43 Fix phrase in HistoryJoined for supergroups. 2017-07-05 16:23:52 +03:00
John Preston
4d3e7ac30c Fix applying a draft with a reply in it.
Fixes #3407
2017-07-05 16:20:34 +03:00
John Preston
7d342b9c6d Add search to channel / supergroup recent actions. 2017-07-05 16:11:08 +03:00
John Preston
e1709c11da Check weak pointer in AddParticipantBoxController.
This may fix some crashes.
2017-07-05 12:39:21 +03:00
John Preston
6d3a6d20b5 Version 1.1.10: Fix build for Xcode and GCC. 2017-07-04 19:17:44 +03:00
John Preston
91a6853f3c Version 1.1.10: Update langs. 2017-07-04 18:40:59 +03:00
John Preston
60d886a59a Version 1.1.10.
- Filter added to channel and supergroup event log.
- Search by username in privacy exceptions editor fixed.
- Adding admins in channels fixed.
2017-07-04 18:32:05 +03:00
John Preston
397797aac7 Add default changelog for the new stable version.
Also add a non-translated changelog for the new alpha/beta versions.
2017-07-04 18:32:04 +03:00
John Preston
61461d0a87 Improve boxes with checks / radios layout. 2017-07-04 16:59:00 +03:00
John Preston
0ae661edf0 Add channel / supergroup admin event log filter. 2017-07-04 16:31:18 +03:00
John Preston
fc6aa288c2 Show all returned rows in RevokePublicLinkBox.
Fixes #3617
2017-07-03 16:57:46 +03:00
John Preston
58dcba71a4 Switch to PeerListBox in channel admin management.
This will fix the problem with adding admins to channels.
Also this will add local search to channel admins list.
Fixes #3615
2017-07-03 16:17:52 +03:00
John Preston
5ca3a81fe2 Fix global search by username in PeerListBox. 2017-07-03 16:17:44 +03:00
John Preston
f316e3bd17 Add debug logs for window position and autoupdate. 2017-07-03 15:23:41 +03:00
John Preston
ebb10fb8ed Improve night mode theme.
Also fix custom notification border painting with reply button.
2017-07-03 13:51:38 +03:00
John Preston
d08dbb835f Paint admin stars with different color in profile. 2017-07-03 13:31:37 +03:00
John Preston
0d38106ed0 Fix Edit option display for supergroup creator.
Supergroup creator saw the Edit option for other users messages.
2017-07-03 13:13:32 +03:00
Viktor Oreshkin
bca0e1e16c Fix remove option in normal groups
If user invites X to group and then X becomes an admin, user won't be
able to remove X anymore, so remove option shouldn't be shown.

Signed-off-by: Viktor Oreshkin <imselfish@stek29.rocks>
2017-07-02 22:29:24 +03:00
John Preston
96d1fe336a Fix assertion violation when editing an admin.
There is a possibility that an EditAdminBox will be shown for someone
who can't add admins right now (in case server says he can edit one).

In that case assertion about the admins checkbox is false and we fix
that. Currently server sometimes does return this flag by mistake.
2017-07-02 22:26:24 +03:00
John Preston
305e15dd03 Fix service message date in admin event log. 2017-07-02 22:26:23 +03:00
John Preston
22287d4c03 Version 1.1.9: Update langs. 2017-06-30 16:02:52 +03:00
John Preston
341b44119f Version 1.1.9.
- Supergroups can now have up to 10.000 members.
- Appoint supergroup admins with granular rights.
Choose who can add users, manage messages, block members,
edit group info & username, add new admins, etc.
- Restrict and ban supergroup members with granular precision.
Read-only bans, GIF & sticker bans, media bans,
temporary bans and restrictions.
- Check the new event log to see all service actions taken by
members and admins of a channel or supergroup in the last 48 hours.
- Toggle night mode in the main menu.
2017-06-30 15:08:29 +03:00
John Preston
0798a0148a Don't change custom background in night mode.
This will at least make it a bit better in #3598.

The real solution with remembering custom backgrounds for both
night mode on and off doesn't fit with current theming way too well.
2017-06-30 15:03:51 +03:00
John Preston
51c0df79fe Support emoji display in PeerListBox status texts. 2017-06-30 14:36:13 +03:00
John Preston
42febdb240 Fix alpha version changelog notification.
We should call window->serviceNotification() because it checks for
the service chat history being loaded before adding the message.

The method in MainWidget was renamed to prevent bugs in the future.
2017-06-30 14:30:11 +03:00
John Preston
5fe1175602 Fix crash when message for forward is deleted.
The messages prepared for forwarding are not a map (MsgId -> item),
but just a map (random int -> item), so we need to loop over them.
2017-06-30 13:46:41 +03:00
John Preston
bf57a1506f Fix assertion violation in the admin events log.
Add support for a log entry about group/channel photo being removed.
2017-06-30 13:35:35 +03:00
John Preston
7b496b3741 Improve in-app changelogs for alpha version. 2017-06-30 13:32:10 +03:00
John Preston
197cdc3928 Alpha 1.1.8.
- Toggle night mode in the main menu.
2017-06-30 09:23:31 +03:00
John Preston
8d14112c5a Update langs. 2017-06-30 09:21:46 +03:00
John Preston
61659244b7 Add night mode switch to the main menu.
Also fix a bug with the default background applying.
2017-06-30 09:21:46 +03:00
John Preston
19023b4cc2 Display a toggle in Menu for Checkable actions. 2017-06-30 09:21:46 +03:00
John Preston
5dcf341aaa Display "no users" in empty add participant box. 2017-06-30 09:21:45 +03:00
John Preston
4993ff1921 Finalize rename / move. 2017-06-30 09:21:45 +03:00
John Preston
d1536d0572 Rename / move history and historywidget modules. 2017-06-30 09:21:45 +03:00
John Preston
836fe119d9 Improve display of admin event log entries.
Don't show the Forward, Delete and Go to message in MediaView.
Better handle round video floating player in admin event log.
2017-06-30 09:21:45 +03:00
John Preston
0b3644b334 Improve phrase on copying selected items as text.
This is a better implementation of #3346.
2017-06-30 09:21:45 +03:00
John Preston
7357119b1a Fix scroll top overflow. Fixes #3524 2017-06-30 09:21:45 +03:00
John Preston
545915e481 Fix name/fwd/reply/viabot info display for media. 2017-06-30 09:21:45 +03:00
John Preston
6f90dc2374 Fix empty admin event log display. 2017-06-30 09:21:45 +03:00
John Preston
91dcfff423 Add unpinned message admin log event action. 2017-06-30 09:21:45 +03:00
John Preston
58b4798b79 Fix tooltip hiding when window is hidden by Cmd+H.
For some reason deleteLater() called for the tooltip from the main
window deactivation signal is really handled only on app quit :/
So now we use InvokeQueued(p, [p] { delete p; }); there instead.

Fixes #3461
2017-06-30 09:21:45 +03:00
John Preston
843d63c221 Fix edit channel controls layout. 2017-06-30 09:21:44 +03:00
John Preston
3b864d63b9 Fix crash in audio player destructor. 2017-06-30 09:21:44 +03:00
John Preston
2a51e2aa59 Workaround GCC bug and fix Linux build. 2017-06-30 09:21:44 +03:00
John Preston
67f2daa43c Closed beta 1001007003: Fix build for old OS X. 2017-06-30 09:21:44 +03:00
John Preston
c3db57a4fc Closed beta 1001007003: Fix Xcode build. 2017-06-30 09:21:44 +03:00
John Preston
885b82b437 Closed beta 1001007003. 2017-06-30 09:21:44 +03:00
John Preston
6487d91722 Add users to a channel check by admin rights.
Also add an admin right checkbox for add users in channels.
2017-06-30 09:21:44 +03:00
John Preston
5b4f70ff96 Fix crash in layer stack unwinding. 2017-06-30 09:21:44 +03:00
John Preston
75fdd5315f Add some more colors for theming. 2017-06-30 09:21:44 +03:00
John Preston
8ee5c70708 Fix admin custom status text in supergroups. 2017-06-30 09:21:44 +03:00
John Preston
b398444b91 Show manage banned and restricted to creator. 2017-06-30 09:21:44 +03:00
John Preston
9e6f2a5d2e Remember connection type settings. 2017-06-30 09:21:43 +03:00
John Preston
7245319351 Prefer static dc options when using a proxy. 2017-06-30 09:21:43 +03:00
John Preston
82912f4a0b Handle socks internal links. 2017-06-30 09:21:43 +03:00
John Preston
1968ca07de Display connecting to proxy state with a link. 2017-06-30 09:21:43 +03:00
John Preston
8c04bed572 Display empty event log placeholder.
Also hide the filter button for now.
2017-06-30 09:21:43 +03:00
John Preston
ae56c5266f Fix rich text entities in log entry original. 2017-06-30 09:21:43 +03:00
John Preston
0a9db8533b Save admin log state to memento and restore it. 2017-06-30 09:21:43 +03:00
John Preston
85e234938d Fix inline keyboard. Disable it in the event log.
Also disable game preview click handler in the event log.
2017-06-30 09:21:43 +03:00
John Preston
816ee794e8 Event log context menu + improved text selection. 2017-06-30 09:21:43 +03:00
John Preston
624f33c5e2 Allow to select and copy text in the events log.
Also better handle window resize in the events log.
2017-06-30 09:21:42 +03:00
John Preston
693c30d264 Handle mouse events in log entry message parts.
Also highlight mentions and hashtags in them.
2017-06-30 09:21:42 +03:00
John Preston
17e08f9291 Separate theme keys for emoji category icon color. 2017-06-30 09:21:42 +03:00
John Preston
cedf8a65e7 Better channel log entry layout inside messages.
Also move HistoryService class to a separate module.
2017-06-30 09:21:42 +03:00
John Preston
0a39e7e2b1 Support userpics and dates in channel log events. 2017-06-30 09:21:42 +03:00
John Preston
5c87b42135 Use plain HistoryItem in channel admin events log.
Instead of using a complex AdminLog::Item use just HistoryItem*.
2017-06-30 09:21:42 +03:00
John Preston
e39b95175b Handle channel event log mouse events. 2017-06-30 09:21:42 +03:00
John Preston
1791b251ad Fix tabbed section / panel render glitches. 2017-06-30 09:21:42 +03:00
John Preston
507b7d7193 Add to each history item its own width value. 2017-06-30 09:21:41 +03:00
John Preston
839e59075d Display log entry original data in HistoryMessage. 2017-06-30 09:21:41 +03:00
John Preston
4962fdf5ae Add phrases and layout for all events in log. 2017-06-30 09:21:41 +03:00
John Preston
fee8690ca6 Channel action log items display and layout. 2017-06-30 09:21:41 +03:00
John Preston
25a718c54b Start recent channel actions log section. 2017-06-30 09:21:41 +03:00
John Preston
401bc86f27 Closed beta 1001007002: New channel rights.
Also fix build in Xcode.
2017-06-30 09:21:41 +03:00
John Preston
c3ad0ae129 Always show that bot-admin reads all messages.
A bot with any admin rights has access to messages.
2017-06-30 09:21:41 +03:00
John Preston
8fe56b9a7d Show blocked / restricted users to all admins. 2017-06-30 09:21:41 +03:00
John Preston
1b649299ff Show edit admin non-mutual-contact error message. 2017-06-30 09:20:31 +03:00
John Preston
6ba57e713e Use forwarded messages only for one selected chat.
Different chats now can have different forwarded "drafts".
2017-06-29 11:40:26 +03:00
John Preston
758cf0388e Edit who can add users to supergroup.
Add a couple of radiobuttons to EditChannelBox for that.
Also a 'change info' admin now can edit 'signatures' in a channel.
2017-06-29 11:40:25 +03:00
John Preston
9de95cee23 Drop link preview if you can't send them.
Just drop the preview when message with a web page preview is
forwarded and we're restricted to send them in the megagroup.
2017-06-29 11:40:23 +03:00
John Preston
b35d2505a4 Better mentions highlighting in PeerListBox. 2017-06-29 11:40:23 +03:00
John Preston
240ced395b Apply changes in ParticipantsBoxController.
When we add admin / banned / restricted user in a channel using
AddParticipantBoxController we now apply the added user in the box.
2017-06-29 11:40:22 +03:00
John Preston
9e8e49b8d9 Add other search to admin/ban/restrict in channel.
Search in chats / contacts / global in AddParticipantBoxController.
Also move all channel participants box controllers to another module.
2017-06-29 11:40:21 +03:00
John Preston
b76bfe2008 Add admin/banned/restricted box using PeerListBox. 2017-06-29 11:40:20 +03:00
John Preston
3dd26dac6e Show supergroup / channel admins in PeerListBox.
Also show "Creator" / "Promoted by" status for those admins.
2017-06-29 11:40:19 +03:00
John Preston
04e587b999 Hide Add Users checkbox if megagroup is democracy.
Also unite invite_users and invite_link flags.
2017-06-29 11:40:17 +03:00
John Preston
adcecaa195 Make search in restricted/blocked box paginated. 2017-06-29 11:40:17 +03:00
John Preston
6fe0fe6fd6 Use lightButtonFg[Over] for Hide All Notifications.
Fixes #3521.
2017-06-29 11:40:15 +03:00
John Preston
7fdac9cd94 Add restricted users box like kicked users box.
Also allow server-side search inside restricted users list.
Also allow server-side search inside kicked users list.
Also allow PeerListController to work not only in PeerListBox.
2017-06-29 11:40:14 +03:00
John Preston
5c0a1bafe2 Add supergroup restriction placeholders / labels. 2017-06-29 11:40:13 +03:00
John Preston
7d2d5c6100 Edit admin and restricted rights in channels. 2017-06-29 11:40:11 +03:00
John Preston
513a9f8d45 Add rich supergroup/channel admin support. 2017-06-29 11:40:10 +03:00
John Preston
330b4a0b00 API scheme updated to layer 68. 2017-06-29 11:40:08 +03:00
John Preston
8ae159dd66 Add special dc options config request. 2017-06-29 11:40:07 +03:00
John Preston
2de96682db Fix phone call decline reason.
If the call was declined fast enough the reason was hangup while it
should be busy.
2017-06-29 11:40:06 +03:00
John Preston
80273c57d0 Fix jumping to an old group message before migration.
Fixes #3513.
2017-06-29 11:40:05 +03:00
John Preston
fe3c33fb52 Change a phrase for the userpic photo area selection.
Fixes #3390.
2017-06-29 11:40:04 +03:00
John Preston
c9a26fd006 Fix call panel with empty user photo on Retina. 2017-06-29 11:40:03 +03:00
John Preston
11d4c50202 Closed beta 1001007001: Fix build in GCC. 2017-06-29 11:40:02 +03:00
John Preston
7dd50fa931 Closed beta 1001007001: Fix build in Xcode. 2017-06-29 11:40:01 +03:00
John Preston
5d0e89db5d Switch to the server side changelogs. 2017-06-29 11:40:00 +03:00
John Preston
d9785f6071 Closed beta 1001007001.
First cloud langpacks version.
2017-06-29 11:39:59 +03:00
John Preston
f3e65d400d Display languages native names in the box.
Also don't suggest the old official languages in a popup.
2017-06-29 11:39:58 +03:00
John Preston
85e6f55536 Support new plural keys format.
All the old plural phrases were changed to work with the new format.
2017-06-29 11:39:56 +03:00
John Preston
b6046d829f Save the default language after logging in.
We suggest an appropriate language in the intro if server has users
system language langpack. But if user doesn't switch to it we save
the default ("en") language so that he won't be bothered again.
2017-06-29 11:39:55 +03:00
John Preston
665a1acfd8 Apply language with app restart if logged in.
Confirm and restart if logged in, just quickly apply if not.
2017-06-29 11:39:54 +03:00
John Preston
d51fd7b3ad Update intro change language link.
Also now MTP::Sender isn't bound to MTP::Instance, because Intro
Widget is MTP::Sender, but Intro::PhoneWidget destroys current
MTP::Instance.
2017-06-29 11:39:53 +03:00
John Preston
b94099e25b Realtime UI translation in all fields and buttons. 2017-06-29 11:39:51 +03:00
John Preston
5fc4f4ed36 Realtime UI translation in Intro.
Also support realtime translation testing by F7-F6-F7-F8 keys.
2017-06-29 11:39:49 +03:00
John Preston
d47a38dfcf Show change language link in intro. 2017-06-29 11:39:48 +03:00
John Preston
f5353080e7 Use Qt UI language if system was not determined.
Also rename platform/mac/specific_mac.cpp to .mm
2017-06-29 11:39:47 +03:00
John Preston
6fb980ca79 Improve custom language loading.
Apply custom language without relaunching the app.
2017-06-29 11:39:46 +03:00
John Preston
3f0b57ec11 Retranslate Settings when language is changed.
Also suggest user to change language from 'en' to his for one time.
2017-06-29 11:39:45 +03:00
John Preston
f5dfeb0c50 Support cloud languages list and switching.
Add Lang::Current().updated() observable for retranslating the UI.
2017-06-29 11:39:43 +03:00
John Preston
139d4e72b5 Start cloud langpack support.
Change the way langpacks are stored.
Support custom langpacks in the new storage.
2017-06-29 11:39:42 +03:00
John Preston
2334ba1fe1 Use QString + Lang::Tag() instead of Lang::String. 2017-06-29 11:39:40 +03:00
John Preston
110e7c8074 Finalize rename / move. 2017-06-29 11:39:38 +03:00
John Preston
f643446d1b Rename / move lang files to lang/ subdir.
Next commit fixes the build.
2017-06-29 11:39:36 +03:00
John Preston
a813793007 API scheme updated to layer 67. 2017-06-29 11:39:35 +03:00
John Preston
972fdb5c76 Fix _historyDown button position.
This button is a child widget of _scroll so the position is relative.
2017-06-29 11:39:34 +03:00
John Preston
0e4b057220 Fix transparent reply previews.
The transparent reply previews (for stickers) were not filled by
transparent background before resizing, now there is a flag for that.
2017-06-29 11:39:33 +03:00
John Preston
6869cc7d04 Fix non-square reply preview in selected messages.
After introducing video messages and sticker reply previews they may
be not a rounded rectangle but any transparent image. So instead of
painting a selected rounded rect over them we just colorize them with
an overlay color like it is done with the selected sticker images.
2017-06-29 11:39:32 +03:00
Viktor Oreshkin
68ddabea5c Don't clear undo history when applying draft
Signed-off-by: Viktor Oreshkin <imselfish@stek29.rocks>
2017-06-29 11:10:13 +03:00
Zankio
60c84bbf51 Add hime inputcontext plugin for linux version
(#3129)

Signed-off-by: Zankio <xxoojoeooxx1@gmail.com>
(github: zankio)
2017-06-29 10:49:31 +03:00
Nikita K
4c61f27826 Fix inconsistency of max width for message content
Signed-off-by: Nikita Kalashnikov <mendor@yuuzukiyo.net> (github: Mendor)
2017-06-28 19:36:18 +03:00
Viktor Oreshkin
3f3b9f3608 Show date from fwdHeader on tooltip
Signed-off-by: Viktor Oreshkin <imselfish@stek29.rocks>
2017-06-28 19:21:34 +03:00
Eduardo Sánchez Muñoz
2eff766e88 Always use UTF-8 to open files in list_sources.py.
This avoids problems with some locale settings.

Signed-off-by: Eduardo Sánchez Muñoz <eduardosanchezmunoz@gmail.com> (github: eduardosm)
2017-06-28 19:19:44 +03:00
Viktor Oreshkin
8b54910538 Fix markdown in issue template
Signed-off-by: Viktor Oreshkin <imselfish@stek29.rocks>
2017-06-06 22:48:28 +03:00
John Preston
66662e02a6 Fix crash in video player seeking.
Regression was introduced in 87ff770020.

Seek handler may destroy controller so first the playback is updated.
2017-05-31 11:58:43 +03:00
399 changed files with 64324 additions and 10920 deletions

View File

@@ -21,7 +21,7 @@ Tell us what happens instead
**Used theme**:
<details><summary>**Logs**:</summary>
<details><summary><b>Logs</b>:</summary>
Insert logs here (if necessary)
<!-- You can type `debugmode` in settings and then see ~/.TelegramDesktop/DebugLogs/log_...txt for log files.

View File

@@ -28,14 +28,15 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
* liblzma ([public domain](http://tukaani.org/xz/))
* Google Breakpad ([License](https://chromium.googlesource.com/breakpad/breakpad/+/master/LICENSE))
* Google Crashpad ([Apache License 2.0](https://chromium.googlesource.com/crashpad/crashpad/+/master/LICENSE))
* GYP ([BSD license](https://github.com/bnoordhuis/gyp/blob/master/LICENSE))
* GYP ([BSD License](https://github.com/bnoordhuis/gyp/blob/master/LICENSE))
* Ninja ([Apache License 2.0](https://github.com/ninja-build/ninja/blob/master/COPYING))
* OpenAL Soft ([LGPL](http://kcat.strangesoft.net/openal.html))
* Opus codec ([BSD license](http://www.opus-codec.org/license/))
* Opus codec ([BSD License](http://www.opus-codec.org/license/))
* FFmpeg ([LGPL](https://www.ffmpeg.org/legal.html))
* Guideline Support Library ([MIT License](https://github.com/Microsoft/GSL/blob/master/LICENSE))
* Mapbox Variant ([BSD license](https://github.com/mapbox/variant/blob/master/LICENSE))
* Mapbox Variant ([BSD License](https://github.com/mapbox/variant/blob/master/LICENSE))
* Open Sans font ([Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html))
* Emoji alpha codes ([MIT License](https://github.com/emojione/emojione/blob/master/extras/alpha-codes/LICENSE.md))
## Build instructions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 MiB

After

Width:  |  Height:  |  Size: 3.2 MiB

View File

@@ -116,7 +116,7 @@ msgReplyBarPos: point(1px, 0px);
msgReplyBarSize: size(2px, 36px);
msgReplyBarSkip: 10px;
msgServicePadding: margins(12px, 3px, 12px, 4px);
msgServiceMargin: margins(10px, 10px, 80px, 2px);
msgServiceMargin: margins(10px, 10px, 10px, 2px);
msgDateSpace: 12px;
msgDateDelta: point(2px, 5px);
@@ -239,7 +239,7 @@ dragPadding: margins(20px, 10px, 20px, 10px);
dragHeight: 72px;
minPhotoSize: 100px;
maxMediaSize: 420px;
maxMediaSize: 430px;
maxStickerSize: 256px;
maxGifSize: 320px;
maxSignatureSize: 144px;

View File

@@ -93,8 +93,13 @@ placeholderFg: windowSubTextFg; // default input field placeholder when field is
placeholderFgActive: #aaaaaa; // default input field placeholder when field is focused
inputBorderFg: #e0e0e0; // default input field bottom border (like in code input field when you log in and field is not focused)
filterInputBorderFg: #54c3f3; // default rounded input field border (like in chats list search field when field is focused)
filterInputInactiveBg: windowBgOver; // default rounded input field background (like in chats list search field when field is inactive)
checkboxFg: #b3b3b3; // default unchecked checkbox rounded rectangle (and also emoji category icons)
filterInputActiveBg: windowBg; // default rounded input field active background (like in chats list search field when field is focused)
filterInputInactiveBg: windowBgOver; // default rounded input field inactive background (like in chats list search field when field is inactive)
checkboxFg: #b3b3b3; // default unchecked checkbox rounded rectangle
botKbBg: menuBgOver; // bot keyboard button background
botKbDownBg: menuBgRipple; // bot keyboard button ripple effect
botKbColor: windowBoldFgOver; // bot keyboard button text
sliderBgInactive: #e1eaef; // default slider not active bar (like in Settings when you choose interface scale or custom notifications count)
sliderBgActive: windowBgActive; // default slider active bar (like in Settings when you choose interface scale or custom notifications count)
@@ -232,8 +237,8 @@ dialogsUnreadBgActive: dialogsTextFgActive; // chat list unread badge background
dialogsUnreadBgMutedActive: dialogsDraftFgActive; // chat list unread badge background for muted chat for current (active) chat
dialogsUnreadFgActive: dialogsBgActive; // chat list unread badge text for current (active) chat
dialogsRippleBg: windowBgRipple;
dialogsRippleBgActive: activeButtonBgRipple;
dialogsRippleBg: windowBgRipple; // chat list background ripple effect
dialogsRippleBgActive: activeButtonBgRipple; // chat list background ripple effect for current (active) chat
dialogsForwardBg: dialogsBgActive; // forwarding panel background (when forwarding messages in the smallest window size)
dialogsForwardFg: dialogsNameFgActive; // forwarding panel text (when forwarding messages in the smallest window size)
@@ -248,6 +253,8 @@ emojiPanBg: windowBg; // emoji panel background
emojiPanCategories: #f7f7f7 | windowBg; // emoji panel categories background
emojiPanHeaderFg: windowSubTextFg; // emoji panel section header text
emojiPanHeaderBg: #fffffff2 | emojiPanBg; // emoji panel section header background
emojiIconFg: checkboxFg; // emoji category icon
emojiIconFgActive: windowBgActive; // active emoji category icon
stickerPanDeleteBg: #000000cc; // delete X button background for custom sent stickers in stickers panel (legacy)
stickerPanDeleteFg: windowFgActive; // delete X button icon for custom sent stickers in stickers panel (legacy)
stickerPreviewBg: #ffffffb0; // sticker and GIF preview background (when you press and hold on a sticker)
@@ -450,7 +457,9 @@ historyComposeButtonBgOver: windowBgOver; // unblock / join channel / mute chann
historyComposeButtonBgRipple: windowBgRipple; // unblock / join channel / mute channel button ripple effect
// overview
overviewCheckBg: #00000040; // shared files / links checkbox background for not selected rows when some rows are selected
overviewCheckBg: #00000040; // shared media / files / links checkbox background for not selected rows when some rows are selected
overviewCheckBgActive: windowBgActive; // shared media / files / links checkbox background for selected rows
overviewCheckBorder: windowBg; // shared media round checkbox border
overviewCheckFg: windowBg; // shared files / links checkbox icon for not selected rows when some rows are selected
overviewCheckFgActive: windowBg; // shared files / links checkbox icon for selected rows
overviewPhotoSelectOverlay: #40ace333; // shared photos / videos / links fill for selected rows
@@ -459,7 +468,10 @@ overviewPhotoSelectOverlay: #40ace333; // shared photos / videos / links fill fo
profileStatusFgOver: #7c99b2; // group members list in group profile user last seen text with mouse over
profileVerifiedCheckBg: windowBgActive; // profile verified check icon background
profileVerifiedCheckFg: windowFgActive; // profile verified check icon tick
profileAdminStartFg: windowBgActive; // group members list admin star icon
profileAdminStartFg: windowBgActive; // group members list creator star icon
profileAdminStarFgOver: profileAdminStartFg; // group members list creator star icon with mouse over
profileOtherAdminStarFg: windowSubTextFg; // group members list admin star icon
profileOtherAdminStarFgOver: profileStatusFgOver; // group members list admin star icon with mouse over
// settings
notificationsBoxMonitorFg: windowFg; // custom notifications settings box monitor color

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_menu_update" = "Update";
"lng_menu_restart" = "Restart";
"lng_menu_back" = "Back";
"lng_menu_night_mode" = "Night mode";
"lng_disable_notifications_from_tray" = "Disable notifications";
"lng_enable_notifications_from_tray" = "Enable notifications";
@@ -88,7 +89,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_continue" = "Continue";
"lng_close" = "Close";
"lng_connecting" = "Connecting...";
"lng_reconnecting" = "Reconnect {count:now|in # s|in # s}...";
"lng_connecting_to_proxy" = "Connecting to proxy...";
"lng_connecting_settings" = "Settings";
"lng_reconnecting#one" = "Reconnect in {count} s...";
"lng_reconnecting#other" = "Reconnect in {count} s...";
"lng_reconnecting_try_now" = "Try now";
"lng_status_service_notifications" = "service notifications";
@@ -102,8 +106,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_last_month" = "last seen within a month";
"lng_status_invisible" = "invisible";
"lng_status_lastseen_now" = "last seen just now";
"lng_status_lastseen_minutes" = "last seen {count:_not_used_|# minute|# minutes} ago";
"lng_status_lastseen_hours" = "last seen {count:_not_used_|# hour|# hours} ago";
"lng_status_lastseen_minutes#one" = "last seen {count} minute ago";
"lng_status_lastseen_minutes#other" = "last seen {count} minutes ago";
"lng_status_lastseen_hours#one" = "last seen {count} hour ago";
"lng_status_lastseen_hours#other" = "last seen {count} hours ago";
"lng_status_lastseen_today" = "last seen today at {time}";
"lng_status_lastseen_yesterday" = "last seen yesterday at {time}";
"lng_status_lastseen_date" = "last seen {date}";
@@ -112,24 +118,43 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_connecting" = "connecting...";
"lng_chat_status_unaccessible" = "group is inaccessible";
"lng_chat_status_members" = "{count:no members|# member|# members}";
"lng_chat_status_members_online" = "{count:_not_used_|# member|# members}, {count_online:_not_used_|# online|# online}";
"lng_chat_status_no_members" = "no members";
"lng_chat_status_members#one" = "{count} member";
"lng_chat_status_members#other" = "{count} members";
"lng_chat_status_online#one" = "{count} online";
"lng_chat_status_online#other" = "{count} online";
"lng_chat_status_members_online" = "{members_count}, {online_count}";
"lng_channel_status" = "channel";
"lng_group_status" = "group";
"lng_channel_members_link" = "{count:_not_used_|# member|# members}";
"lng_channel_admins_link" = "{count:_not_used_|# administrator|# administrators}";
"lng_channel_members_link#one" = "{count} member";
"lng_channel_members_link#other" = "{count} members";
"lng_channel_admins_link#one" = "{count} administrator";
"lng_channel_admins_link#other" = "{count} administrators";
"lng_server_error" = "Internal server error.";
"lng_flood_error" = "Too many tries. Please try again later.";
"lng_gif_error" = "An error has occured while reading GIF animation :(";
"lng_gif_error" = "An error has occurred while reading GIF animation :(";
"lng_edit_error" = "You cannot edit this message";
"lng_join_channel_error" = "Sorry, you have joined too many channels and supergroups. Please leave some before joining.";
"lng_error_phone_flood" = "Sorry, you have deleted and re-created your account too many times recently. Please wait for a few days before signing up again.";
"lng_error_start_minimized_passcoded" = "You have set a local passcode, so the app can't be launched minimized. App will ask you to enter the passcode before it can start working.";
"lng_error_pinned_max" = "Sorry, you can pin no more than {count:_not_used_|# chat|# chats} to the top.";
"lng_error_pinned_max#one" = "Sorry, you can pin no more than {count} chat to the top.";
"lng_error_pinned_max#other" = "Sorry, you can pin no more than {count} chats to the top.";
"lng_error_public_groups_denied" = "Unfortunately, you were banned from participating in public groups.\n{more_info}";
"lng_error_cant_edit_admin" = "Sorry, you can't edit permissions for this admin.";
"lng_error_cant_add_member" = "Sorry, you can't add the bot to this group. Ask a group admin to do it.";
"lng_error_cant_add_bot" = "Sorry, this bot can't be added to groups.";
"lng_error_cant_add_admin_invite" = "Sorry, you can't add this user as an admin because they are not a member of this group and you are not allowed to invite them.";
"lng_error_cant_add_admin_unban" = "Sorry, you can't add this user as an admin because they are in the blacklist and you can't unban them.";
"lng_error_cant_ban_admin" = "Sorry, you can't ban this user because they are an admin in this group and you are not allowed to demote them.";
"lng_sure_add_admin_invite" = "This user is not a member of this group. Add them to the group and promote them to admin?";
"lng_sure_add_admin_unban" = "This user is currently restricted or banned in this group. Are you sure you want to unban and promote them?";
"lng_sure_ban_admin" = "This user is an admin in this group. Are you sure you want to go ahead and restrict them?";
"lng_sure_ban_user_group" = "Ban {user} in the group?";
"lng_sure_enable_socks" = "Are you sure you want to enable this proxy?\n\nServer: {server}\nPort: {port}\n\nYou can change your proxy server later in the Settings (Connection Type).";
"lng_sure_enable" = "Enable";
"lng_edit_deleted" = "This message was deleted";
"lng_edit_too_long" = "Your message text is too long";
@@ -198,9 +223,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_signin_sure_reset" = "Warning!\n\nYou will lose all your chats and messages, along with any media and files you shared!\n\nDo you want to reset your account?";
"lng_signin_reset" = "Reset";
"lng_signin_reset_wait" = "Since the account {phone_number} is active and protected by a password, we will delete it in 1 week for security purposes. You can cancel this process at any time.\n\nYoull be able to reset your account in:\n{when}";
"lng_signin_reset_in_days" = "{count_days:0 days|# day|# days} {count_hours:0 hours|# hour|# hours} {count_minutes:0 minutes|# minute|# minutes}";
"lng_signin_reset_in_hours" = "{count_hours:0 hours|# hour|# hours} {count_minutes:0 minutes|# minute|# minutes}";
"lng_signin_reset_in_minutes" = "{count_minutes:0 minutes|# minute|# minutes}";
"lng_signin_reset_days#one" = "{count} day";
"lng_signin_reset_days#other" = "{count} days";
"lng_signin_reset_hours#one" = "{count} hour";
"lng_signin_reset_hours#other" = "{count} hours";
"lng_signin_reset_minutes#one" = "{count} minute";
"lng_signin_reset_minutes#other" = "{count} minutes";
"lng_signin_reset_in_days" = "{days_count} {hours_count} {minutes_count}";
"lng_signin_reset_in_hours" = "{hours_count} {minutes_count}";
"lng_signin_reset_cancelled" = "Your recent attempts to reset this account have been cancelled by its active user. Please try again in 7 days.";
"lng_signup_title" = "Your Info";
@@ -223,7 +253,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_settings_save" = "Save";
"lng_settings_upload" = "Set Profile Photo";
"lng_settings_crop_profile" = "Select a square area for your profile photo";
"lng_settings_crop_profile" = "Select an area for your profile photo";
"lng_settings_uploading_photo" = "Uploading photo...";
"lng_settings_edit" = "Edit";
"lng_settings_drop_area_subtitle" = "to set it as your photo";
@@ -241,10 +271,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_username_link" = "This link opens a chat with you:";
"lng_username_copied" = "Link copied to clipboard.";
"lng_bio_title" = "Edit your bio";
"lng_bio_placeholder" = "Bio";
"lng_bio_about" = "You can add a few lines about yourself. Anyone who opens your profile will see this text.";
"lng_settings_section_info" = "Info";
"lng_settings_phone_number" = "Phone number:";
"lng_settings_username" = "Username:";
"lng_settings_choose_username" = "Choose username";
"lng_settings_empty_bio" = "None";
"lng_settings_section_notify" = "Notifications";
"lng_settings_desktop_notify" = "Desktop notifications";
@@ -303,7 +338,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_backgrounds_header" = "Choose your new chat background";
"lng_theme_sure_keep" = "Keep this color theme?";
"lng_theme_reverting" = "Reverting to the old color theme in {count:_not_used_|# second|# seconds}.";
"lng_theme_reverting#one" = "Reverting to the old color theme in {count} second.";
"lng_theme_reverting#other" = "Reverting to the old color theme in {count} seconds.";
"lng_theme_keep_changes" = "Keep changes";
"lng_theme_revert" = "Revert";
@@ -329,14 +365,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_local_storage_title" = "Local storage";
"lng_settings_no_data_cached" = "No cached data found!";
"lng_settings_images_cached" = "{count:_not_used_|# image|# images}, {size}";
"lng_settings_audios_cached" = "{count:_not_used_|# voice message|# voice messages}, {size}";
"lng_settings_images_cached#one" = "{count} image, {size}";
"lng_settings_images_cached#other" = "{count} images, {size}";
"lng_settings_audios_cached#one" = "{count} voice message, {size}";
"lng_settings_audios_cached#other" = "{count} voice messages, {size}";
"lng_local_storage_clear" = "Clear all";
"lng_local_storage_clearing" = "Clearing...";
"lng_local_storage_cleared" = "Cleared!";
"lng_local_storage_clear_failed" = "Clear failed :(";
"lng_settings_section_advanced_settings" = "Advanced Settings";
"lng_settings_enable_night_theme" = "Enable night mode";
"lng_settings_disable_night_theme" = "Disable night mode";
"lng_passcode_remove_button" = "Remove";
@@ -348,8 +388,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_passcode_autolock" = "Auto-Lock";
"lng_passcode_autolock_away" = "Auto-Lock if away for:";
"lng_passcode_autolock_inactive" = "Auto-Lock if inactive for:";
"lng_passcode_autolock_minutes" = "{count:_not_used_|# minute|# minutes}";
"lng_passcode_autolock_hours" = "{count:_not_used_|# hour|# hours}";
"lng_passcode_autolock_minutes#one" = "{count} minute";
"lng_passcode_autolock_minutes#other" = "{count} minutes";
"lng_passcode_autolock_hours#one" = "{count} hour";
"lng_passcode_autolock_hours#other" = "{count} hours";
"lng_passcode_enter_old" = "Enter current passcode";
"lng_passcode_enter_first" = "Enter a passcode";
"lng_passcode_enter_new" = "Enter new passcode";
@@ -455,32 +497,46 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_privacy_lastseen_title" = "Last seen privacy";
"lng_edit_privacy_lastseen_description" = "You can choose who can see your last seen time:";
"lng_edit_privacy_lastseen_warning" = "Important: you won't be able to see Last Seen times for people with whom you don't share your Last Seen time. Approximate last seen will be shown instead (recently, within a week, within a month).";
"lng_edit_privacy_lastseen_always" = "Always share with{count:| # user| # users}";
"lng_edit_privacy_lastseen_never" = "Never share with{count:| # user| # users}";
"lng_edit_privacy_lastseen_always_empty" = "Always share with";
"lng_edit_privacy_lastseen_always#one" = "Always share with {count} user";
"lng_edit_privacy_lastseen_always#other" = "Always share with {count} users";
"lng_edit_privacy_lastseen_never_empty" = "Never share with";
"lng_edit_privacy_lastseen_never#one" = "Never share with {count} user";
"lng_edit_privacy_lastseen_never#other" = "Never share with {count} users";
"lng_edit_privacy_lastseen_exceptions" = "These settings will override the values above.";
"lng_edit_privacy_lastseen_always_title" = "Always share with";
"lng_edit_privacy_lastseen_never_title" = "Never share with";
"lng_edit_privacy_groups_title" = "Group invite settings";
"lng_edit_privacy_groups_description" = "You can choose who can add you to groups and channels with granular precision:";
"lng_edit_privacy_groups_always" = "Always allow{count:| # user| # users}";
"lng_edit_privacy_groups_never" = "Never allow{count:| # user| # users}";
"lng_edit_privacy_groups_always_empty" = "Always allow";
"lng_edit_privacy_groups_always#one" = "Always allow {count} user";
"lng_edit_privacy_groups_always#other" = "Always allow {count} users";
"lng_edit_privacy_groups_never_empty" = "Never allow";
"lng_edit_privacy_groups_never#one" = "Never allow {count} user";
"lng_edit_privacy_groups_never#other" = "Never allow {count} users";
"lng_edit_privacy_groups_exceptions" = "These users will or will not be able to add you to groups and channels regardless of the settings above.";
"lng_edit_privacy_groups_always_title" = "Always allow";
"lng_edit_privacy_groups_never_title" = "Never allow";
"lng_edit_privacy_calls_title" = "Phone calls privacy";
"lng_edit_privacy_calls_description" = "You can restrict who can call you:";
"lng_edit_privacy_calls_always" = "Always allow{count:| # user| # users}";
"lng_edit_privacy_calls_never" = "Never allow{count:| # user| # users}";
"lng_edit_privacy_calls_always_empty" = "Always allow";
"lng_edit_privacy_calls_always#one" = "Always allow {count} user";
"lng_edit_privacy_calls_always#other" = "Always allow {count} users";
"lng_edit_privacy_calls_never_empty" = "Never allow";
"lng_edit_privacy_calls_never#one" = "Never allow {count} user";
"lng_edit_privacy_calls_never#other" = "Never allow {count} users";
"lng_edit_privacy_calls_exceptions" = "These users will or will not be able to call you regardless of the settings above.";
"lng_edit_privacy_calls_always_title" = "Always allow";
"lng_edit_privacy_calls_never_title" = "Never allow";
"lng_self_destruct_title" = "Account self-destruction";
"lng_self_destruct_description" = "If you don't come online at least once within this period, your account will be deleted along with all groups, messages and contacts.";
"lng_self_destruct_months" = "{count:_not_used_|# month|# months}";
"lng_self_destruct_years" = "{count:_not_used_|# year|# years}";
"lng_self_destruct_months#one" = "{count} month";
"lng_self_destruct_months#other" = "{count} months";
"lng_self_destruct_years#one" = "{count} year";
"lng_self_destruct_years#other" = "{count} years";
"lng_change_phone_title" = "Change phone number";
"lng_change_phone_description" = "You can change your Telegram number\nhere. Your account and all your cloud data\n— messages, media, contacts, etc. will be\nmoved to the new number.\n\n[b]Important[/b]: all your Telegram contacts will\nget your [b]new number[/b] added to their address\nbook, provided they had your old number and\nyou haven't blocked them in Telegram.";
@@ -506,15 +562,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_invite_link_section" = "Invite link";
"lng_profile_create_public_link" = "Create public link";
"lng_profile_edit_public_link" = "Edit public link";
"lng_profile_search_members" = "Search members";
"lng_profile_manage_admins" = "Manage administrators";
"lng_profile_manage_blocklist" = "Manage blocked users";
"lng_profile_common_groups" = "{count:_not_used_|# group|# groups} in common";
"lng_profile_manage_blocklist" = "Manage banned users";
"lng_profile_manage_restrictedlist" = "Manage restricted users";
"lng_profile_recent_actions" = "Recent actions";
"lng_profile_common_groups#one" = "{count} group in common";
"lng_profile_common_groups#other" = "{count} groups in common";
"lng_profile_common_groups_section" = "Groups in common";
"lng_profile_participants_section" = "Members";
"lng_profile_info_section" = "Info";
"lng_profile_mobile_number" = "Mobile:";
"lng_profile_username" = "Username:";
"lng_profile_link" = "Link:";
"lng_profile_bio" = "Bio:";
"lng_profile_add_contact" = "Add Contact";
"lng_profile_edit_contact" = "Edit";
"lng_profile_enable_notifications" = "Notifications";
@@ -542,23 +603,30 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_delete_and_exit" = "Leave";
"lng_profile_kick" = "Remove";
"lng_profile_admin" = "admin";
"lng_profile_edit_permissions" = "Edit";
"lng_profile_sure_kick" = "Remove {user} from the group?";
"lng_profile_sure_kick_channel" = "Remove {user} from the channel?";
"lng_profile_sure_kick_admin" = "Remove {user} from administrators?";
"lng_profile_loading" = "Loading...";
"lng_profile_shared_media" = "Shared media";
"lng_profile_no_media" = "No media in this conversation.";
"lng_profile_photos" = "{count:_not_used_|# photo|# photos}";
"lng_profile_photos#one" = "{count} photo";
"lng_profile_photos#other" = "{count} photos";
"lng_profile_photos_header" = "Photos";
"lng_profile_videos" = "{count:_not_used_|# video|# videos}";
"lng_profile_videos#one" = "{count} video";
"lng_profile_videos#other" = "{count} videos";
"lng_profile_videos_header" = "Videos";
"lng_profile_songs" = "{count:_not_used_|# audio file|# audio files}";
"lng_profile_songs#one" = "{count} audio file";
"lng_profile_songs#other" = "{count} audio files";
"lng_profile_songs_header" = "Audio files";
"lng_profile_files" = "{count:_not_used_|# file|# files}";
"lng_profile_files#one" = "{count} file";
"lng_profile_files#other" = "{count} files";
"lng_profile_files_header" = "Files";
"lng_profile_audios" = "{count:_not_used_|# voice message|# voice messages}";
"lng_profile_audios#one" = "{count} voice message";
"lng_profile_audios#other" = "{count} voice messages";
"lng_profile_audios_header" = "Voice messages";
"lng_profile_shared_links" = "{count:_not_used_|# shared link|# shared links}";
"lng_profile_shared_links#one" = "{count} shared link";
"lng_profile_shared_links#other" = "{count} shared links";
"lng_profile_shared_links_header" = "Shared links";
"lng_profile_copy_phone" = "Copy Phone Number";
"lng_profile_copy_fullname" = "Copy Name";
@@ -578,16 +646,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_report_button" = "Report";
"lng_report_thanks" = "Thank you! Your report will be reviewed by our team very soon.";
"lng_channel_add_admins" = "New administrator";
"lng_channel_add_members" = "Add members";
"lng_channel_add_banned" = "Ban user";
"lng_channel_add_restricted" = "Restrict user";
"lng_channel_members" = "Members";
"lng_channel_only_last_shown" = "Only the last {count:_not_used_|# member is|# members are} shown here";
"lng_channel_only_last_shown#one" = "Only the last {count} member is shown here";
"lng_channel_only_last_shown#other" = "Only the last {count} members are shown here";
"lng_channel_admins" = "Administrators";
"lng_channel_add_admin" = "Add Administrator";
"lng_channel_admin_sure" = "Add {user} to administrators?";
"lng_channel_admins_too_much" = "Sorry, you have reached the limit of the administrators. Please remove one administrator first.";
"lng_channel_admin_status_creator" = "Creator";
"lng_channel_admin_status_promoted_by" = "Promoted by {user}";
"lng_channel_admin_status_not_admin" = "Not administrator";
"lng_group_blocked_list_about" = "Blocked users are removed from the group and can only come back if invited by an admin.\nInvite links don't work for them.";
"lng_group_blocked_list_about" = "Banned users are removed from the group and can only come back if invited by an admin.\nInvite links don't work for them.";
"lng_chat_all_members_admins" = "All Members Are Admins";
"lng_chat_about_all_admins" = "Group members can add new members, edit name and photo of the group.";
@@ -595,7 +667,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_participant_filter" = "Search";
"lng_participant_invite" = "Invite";
"lng_participant_invite_sorry" = "Sorry, you can only add the first {count:_not_used|# member|# members} to a channel personally.\n\nFrom now on, people will need to join via your invite link.";
"lng_participant_invite_sorry#one" = "Sorry, you can only add the first {count} member to a channel personally.\n\nFrom now on, people will need to join via your invite link.";
"lng_participant_invite_sorry#other" = "Sorry, you can only add the first {count} members to a channel personally.\n\nFrom now on, people will need to join via your invite link.";
"lng_create_group_back" = "Back";
"lng_create_group_next" = "Next";
"lng_create_group_create" = "Create";
@@ -618,8 +691,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_create_channel_link_available" = "This link is available";
"lng_create_channel_link_copied" = "Link copied to clipboard";
"lng_create_group_crop" = "Select a square area for group photo";
"lng_create_channel_crop" = "Select a square area for channel photo";
"lng_create_group_crop" = "Select an area for group photo";
"lng_create_channel_crop" = "Select an area for channel photo";
"lng_failed_add_participant" = "Could not add user. Please try again later.";
"lng_failed_add_not_mutual" = "Sorry, if a person leaves a group, only a mutual contact can bring them back (they need to have your phone number, and you need theirs).";
@@ -637,8 +710,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_message_empty" = "Empty Message";
"lng_message_unsupported" = "This message is not supported by your version of Telegram Desktop. Please update to the last version in Settings or install it from {link}";
"lng_duration_seconds" = "{count:_not_used_|# second|# seconds}";
"lng_duration_minutes_seconds" = "{count_minutes:# min|# min|# min} {count_seconds:# sec|# sec|# sec}";
"lng_duration_seconds#one" = "{count} second";
"lng_duration_seconds#other" = "{count} seconds";
"lng_duration_minsec_minutes#one" = "{count} min";
"lng_duration_minsec_minutes#other" = "{count} min";
"lng_duration_minsec_seconds#one" = "{count} sec";
"lng_duration_minsec_seconds#other" = "{count} sec";
"lng_duration_minutes_seconds" = "{minutes_count} {seconds_count}";
"lng_action_add_user" = "{from} added {user}";
"lng_action_add_users_many" = "{from} added {users}";
@@ -676,18 +754,33 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_action_pinned_media_sticker" = "a sticker";
"lng_action_pinned_media_emoji_sticker" = "a {emoji} sticker";
"lng_action_pinned_media_game" = "the game «{game}»";
"lng_action_game_score" = "{from} scored {count:#|#|#} in {game}";
"lng_action_game_you_scored" = "You scored {count:#|#|#} in {game}";
"lng_action_game_score_no_game" = "{from} scored {count:#|#|#}";
"lng_action_game_you_scored_no_game" = "You scored {count:#|#|#}";
"lng_action_game_score#one" = "{from} scored {count} in {game}";
"lng_action_game_score#other" = "{from} scored {count} in {game}";
"lng_action_game_you_scored#one" = "You scored {count} in {game}";
"lng_action_game_you_scored#other" = "You scored {count} in {game}";
"lng_action_game_score_no_game#one" = "{from} scored {count}";
"lng_action_game_score_no_game#other" = "{from} scored {count}";
"lng_action_game_you_scored_no_game#one" = "You scored {count}";
"lng_action_game_you_scored_no_game#other" = "You scored {count}";
"lng_action_payment_done" = "You have just successfully transferred {amount} to {user}";
"lng_action_payment_done_for" = "You have just successfully transferred {amount} to {user} for {invoice}";
"lng_action_took_screenshot" = "{from} took a screenshot!";
"lng_action_you_took_screenshot" = "You took a screenshot!";
"lng_profile_migrate_reached" = "{count:_not_used_|# member|# members} limit reached";
"lng_ttl_photo_received" = "{from} sent you a self-destructing photo. Please view it on your mobile.";
"lng_ttl_photo_sent" = "You sent a self-destructing photo.";
"lng_ttl_photo_expired" = "Photo has expired";
"lng_ttl_video_received" = "{from} sent you a self-destructing video. Please view it on your mobile.";
"lng_ttl_video_sent" = "You sent a self-destructing video.";
"lng_ttl_video_expired" = "Video has expired";
"lng_profile_migrate_reached#one" = "{count} member limit reached";
"lng_profile_migrate_reached#other" = "{count} members limit reached";
"lng_profile_migrate_body" = "To get over this limit, you can upgrade your group to a supergroup.";
"lng_profile_migrate_learn_more" = "Learn more »";
"lng_profile_migrate_about" = "If you'd like to go over this limit, you can upgrade your group to a supergroup. In supergroups:";
"lng_profile_migrate_feature1" = "— The members limit is {count:_not_used_|# user|# users}";
"lng_profile_migrate_feature1#one" = "— The members limit is {count} user";
"lng_profile_migrate_feature1#other" = "— The members limit is {count} users";
"lng_profile_migrate_feature2" = "— New members see the entire chat history";
"lng_profile_migrate_feature3" = "— Admins delete messages for everyone";
"lng_profile_migrate_feature4" = "— Notifications are muted by default";
@@ -702,7 +795,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_convert_feature4" = "— Creator can set a public link for the group";
"lng_profile_convert_warning" = "{bold_start}Note:{bold_end} This action can not be undone";
"lng_profile_convert_confirm" = "Convert";
"lng_profile_add_more_after_upgrade" = "You will be able to add up to {count:_not_used_|# member|# members} after you upgrade your group to a supergroup.";
"lng_profile_add_more_after_upgrade#one" = "You will be able to add up to {count} member after you upgrade your group to a supergroup.";
"lng_profile_add_more_after_upgrade#other" = "You will be able to add up to {count} members after you upgrade your group to a supergroup.";
"lng_channel_not_accessible" = "Sorry, this channel is not accessible.";
"lng_group_not_accessible" = "Sorry, this group is not accessible.";
@@ -717,7 +811,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_group_invite_want_join_channel" = "Do you want to join the channel «{title}»?";
"lng_group_invite_join" = "Join";
"lng_group_invite_members" = "{count:_not_used_|# member|# members}, among them:";
"lng_group_invite_members#one" = "{count} member, among them:";
"lng_group_invite_members#other" = "{count} members, among them:";
"lng_group_invite_link" = "Invite link:";
"lng_group_invite_create" = "Create an invite link";
@@ -730,6 +825,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_channel_public_link_copied" = "Link copied to clipboard.";
"lng_forwarded" = "Forwarded from {user}";
"lng_forwarded_date" = "Original: {date}";
"lng_forwarded_channel" = "Forwarded from {channel}";
"lng_forwarded_via" = "Forwarded from {user} via {inline_bot}";
"lng_forwarded_channel_via" = "Forwarded from {channel} via {inline_bot}";
@@ -809,7 +905,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_stickers_you_have" = "Manage and reorder sticker packs";
"lng_stickers_featured" = "Trending Stickers";
"lng_stickers_return" = "Undo";
"lng_stickers_count" = "{count:Loading...|# sticker|# stickers}";
"lng_stickers_count#one" = "{count} sticker";
"lng_stickers_count#other" = "{count} stickers";
"lng_stickers_masks_pack" = "This is a pack of mask stickers. You can use them in the photo editor on our mobile apps.";
"lng_in_dlg_photo" = "Photo";
@@ -839,6 +936,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_cant_invite_banned" = "Sorry, only admin can add this user.";
"lng_cant_invite_privacy" = "Sorry, you cannot add this user to groups because of their privacy settings.";
"lng_cant_invite_privacy_channel" = "Sorry, you cannot add this user to channels because of their privacy settings.";
"lng_cant_delete_group#one" = "Sorry, it is currently not possible to manually delete communities that have more than {count} member. Please contact Telegram support if you would like to delete this group.";
"lng_cant_delete_group#other" = "Sorry, it is currently not possible to manually delete communities that have more than {count} members. Please contact Telegram support if you would like to delete this group.";
"lng_cant_delete_channel#one" = "Sorry, it is currently not possible to manually delete communities that have more than {count} member. Please contact Telegram support if you would like to delete this channel.";
"lng_cant_delete_channel#other" = "Sorry, it is currently not possible to manually delete communities that have more than {count} members. Please contact Telegram support if you would like to delete this channel.";
"lng_cant_do_this" = "Sorry, this action is unavailable.";
"lng_send_button" = "Send";
@@ -884,11 +985,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_typing" = "typing";
"lng_user_typing" = "{user} is typing";
"lng_users_typing" = "{user} and {second_user} are typing";
"lng_many_typing" = "{count:_not_used_|# is|# are} typing";
"lng_many_typing#one" = "{count} is typing";
"lng_many_typing#other" = "{count} are typing";
"lng_playing_game" = "playing a game";
"lng_user_playing_game" = "{user} is playing a game";
"lng_users_playing_game" = "{user} and {second_user} are playing a game";
"lng_many_playing_game" = "{count:_not_used_|# is|# are} playing a game";
"lng_many_playing_game#one" = "{count} is playing a game";
"lng_many_playing_game#other" = "{count} are playing a game";
"lng_send_action_record_video" = "recording a video";
"lng_user_action_record_video" = "{user} is recording a video";
"lng_send_action_upload_video" = "sending a video";
@@ -909,7 +1012,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_user_action_geo_location" = "{user} is picking a location";
"lng_send_action_choose_contact" = "choosing a contact";
"lng_user_action_choose_contact" = "{user} is choosing a contact";
"lng_unread_bar" = "{count:_not_used_|# unread message|# unread messages}";
"lng_unread_bar#one" = "{count} unread message";
"lng_unread_bar#other" = "{count} unread messages";
"lng_maps_point" = "Location";
"lng_save_photo" = "Save image";
@@ -932,7 +1036,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_from_top" = "Unpin from top";
"lng_context_promote_admin" = "Promote to admin";
"lng_context_remove_admin" = "Remove from admins";
"lng_context_edit_permissions" = "Edit permissions";
"lng_context_restrict_user" = "Restrict user";
"lng_context_remove_from_group" = "Remove from group";
"lng_context_copy_link" = "Copy Link";
@@ -970,31 +1075,41 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_msg" = "Unpin Message";
"lng_context_cancel_upload" = "Cancel Upload";
"lng_context_copy_selected" = "Copy Selected Text";
"lng_context_copy_selected_items" = "Copy Selected as Text";
"lng_context_forward_selected" = "Forward Selected";
"lng_context_delete_selected" = "Delete Selected";
"lng_context_clear_selection" = "Clear Selection";
"lng_really_send_image" = "Do you want to send this image?";
"lng_really_send_file" = "Do you want to send this file?";
"lng_really_share_contact" = "Do you want to share this contact?";
"lng_send_images_compress" = "Compress {count:_not_used_|image|images}";
"lng_send_images_compress#one" = "Compress image";
"lng_send_images_compress#other" = "Compress images";
"lng_send_image_non_local" = "Could not send a non local file: {name}";
"lng_send_image_empty" = "Could not send an empty file: {name}";
"lng_send_image_too_large" = "Could not send a file, because it is larger than 1500 MB: {name}";
"lng_send_folder" = "Could not send «{name}» because it is a directory :(";
"lng_send_images_selected" = "{count:_not_used_|# image|# images} selected";
"lng_send_photos" = "Send {count:_not_used_|# photo|# photos}";
"lng_send_files_selected" = "{count:_not_used_|# file|# files} selected";
"lng_send_files" = "Send {count:_not_used_|# file|# files}";
"lng_send_images_selected#one" = "{count} image selected";
"lng_send_images_selected#other" = "{count} images selected";
"lng_send_photos#one" = "Send {count} photo";
"lng_send_photos#other" = "Send {count} photos";
"lng_send_files_selected#one" = "{count} file selected";
"lng_send_files_selected#other" = "{count} files selected";
"lng_send_files#one" = "Send {count} file";
"lng_send_files#other" = "Send {count} files";
"lng_forward_choose" = "Choose recipient...";
"lng_forward_cant" = "Sorry, no way to forward here :(";
"lng_forward_confirm" = "Forward to {recipient}?";
"lng_forward_share_contact" = "Share contact to {recipient}?";
"lng_forward_share_cant" = "Sorry, no way to share contact here :(";
"lng_forward_send_file_confirm" = "Send «{name}» to {recipient}?";
"lng_forward_send_files_confirm" = "Send selected files to {recipient}?";
"lng_forward_send_files_cant" = "Sorry, no way to send media here :(";
"lng_forward_send" = "Send";
"lng_forward_messages" = "{count:_not_used_|Forwarded message|# forwarded messages}";
"lng_forwarding_from" = "{user} and {count:_not_used_|# other|# others}";
"lng_forward_messages#one" = "{count} forwarded message";
"lng_forward_messages#other" = "{count} forwarded messages";
"lng_forwarding_from#one" = "{user} and {count} other";
"lng_forwarding_from#other" = "{user} and {count} others";
"lng_forwarding_from_two" = "{user} and {second_user}";
"lng_inline_switch_choose" = "Choose conversation...";
"lng_inline_switch_cant" = "Sorry, no way to write here :(";
@@ -1016,6 +1131,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_contact_title" = "Edit contact name";
"lng_edit_channel_title" = "Edit channel";
"lng_edit_sign_messages" = "Sign messages";
"lng_edit_group_who_invites" = "Who can add members";
"lng_edit_group_invites_everybody" = "All members";
"lng_edit_group_invites_only_admins" = "Only admins";
"lng_edit_group" = "Edit group";
"lng_edit_self_title" = "Edit your name";
"lng_confirm_contact_data" = "New Contact";
@@ -1040,15 +1158,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_selected_clear" = "Cancel";
"lng_selected_delete" = "Delete";
"lng_selected_forward" = "Forward";
"lng_selected_count" = "{count:_not_used_|# message|# messages}";
"lng_selected_count#one" = "{count} message";
"lng_selected_count#other" = "{count} messages";
"lng_selected_cancel_sure_this" = "Cancel uploading?";
"lng_selected_upload_stop" = "Stop";
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
"lng_selected_delete_sure" = "Do you want to delete {count:_not_used_|# message|# messages}?";
"lng_selected_delete_sure#one" = "Do you want to delete {count} message?";
"lng_selected_delete_sure#other" = "Do you want to delete {count} messages?";
"lng_delete_photo_sure" = "Do you want to delete this photo?";
"lng_delete_for_everyone_hint" = "This will delete {count:_not_used_|it|them} for everyone in this chat.";
"lng_delete_for_me_chat_hint" = "This will delete {count:_not_used_|it|them} just for you, not for other participants of the chat.";
"lng_delete_for_me_hint" = "This will delete {count:_not_used_|it|them} just for you.";
"lng_delete_for_everyone_hint#one" = "This will delete it for everyone in this chat.";
"lng_delete_for_everyone_hint#other" = "This will delete them for everyone in this chat.";
"lng_delete_for_me_chat_hint#one" = "This will delete it just for you, not for other participants of the chat.";
"lng_delete_for_me_chat_hint#other" = "This will delete them just for you, not for other participants of the chat.";
"lng_delete_for_me_hint#one" = "This will delete it just for you.";
"lng_delete_for_me_hint#other" = "This will delete them just for you.";
"lng_delete_for_everyone_check" = "Delete for everyone";
"lng_delete_for_other_check" = "Delete for {user}";
"lng_box_delete" = "Delete";
@@ -1060,7 +1183,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_about_text_3" = "Visit {faq_open}Telegram FAQ{faq_close} for more info.";
"lng_about_done" = "Done";
"lng_search_found_results" = "{count:No messages found|Found # message|Found # messages}";
"lng_search_no_results" = "No messages found";
"lng_search_found_results#one" = "Found {count} message";
"lng_search_found_results#other" = "Found {count} messages";
"lng_search_global_results" = "Global search results";
"lng_media_save_progress" = "{ready} of {total} {mb}";
@@ -1169,11 +1294,133 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_player_message_yesterday" = "Yesterday at {time}";
"lng_player_message_date" = "{date} at {time}";
"lng_rights_edit_admin" = "Edit administrator";
"lng_rights_edit_admin_header" = "What can this admin do?";
"lng_rights_about_add_admins_yes" = "This admin will be able to add new admins with the same (or more limited) permissions.";
"lng_rights_about_add_admins_no" = "This admin will not be able to add new admins.";
"lng_rights_about_admin_cant_edit" = "You cannot edit rights of this admin.";
"lng_rights_user_restrictions" = "User restrictions";
"lng_rights_user_restrictions_header" = "What can this user do?";
"lng_rights_channel_info" = "Change channel info";
"lng_rights_channel_post" = "Post messages";
"lng_rights_channel_edit" = "Edit messages of others";
"lng_rights_channel_delete" = "Delete messages of others";
"lng_rights_group_info" = "Change group info";
"lng_rights_group_ban" = "Ban users";
"lng_rights_group_invite_link" = "Invite users via link";
"lng_rights_group_invite" = "Add users";
"lng_rights_group_pin" = "Pin messages";
"lng_rights_group_delete" = "Delete messages";
"lng_rights_add_admins" = "Add new admins";
"lng_rights_chat_read" = "Read messages";
"lng_rights_chat_send_text" = "Send messages";
"lng_rights_chat_send_media" = "Send media";
"lng_rights_chat_send_stickers" = "Send stickers & GIFs";
"lng_rights_chat_send_links" = "Embed links";
"lng_rights_chat_banned_until_header" = "Restricted until";
"lng_rights_chat_banned_forever" = "Forever";
"lng_rights_chat_banned_day#one" = "For {count} day";
"lng_rights_chat_banned_day#other" = "For {count} days";
"lng_rights_chat_banned_week#one" = "For {count} week";
"lng_rights_chat_banned_week#other" = "For {count} weeks";
"lng_rights_chat_banned_custom" = "Custom";
"lng_rights_chat_banned_custom_date" = "Until {date}";
"lng_rights_chat_banned_block" = "Ban and remove from group";
"lng_restricted_send_message" = "The admins of this group restricted you from writing here.";
"lng_restricted_send_media" = "The admins of this group restricted you from posting media content here.";
"lng_restricted_send_stickers" = "The admins of this group restricted you from posting stickers here.";
"lng_restricted_send_gifs" = "The admins of this group restricted you from posting GIFs here.";
"lng_restricted_send_inline" = "The admins of this group restricted you from posting inline content here.";
"lng_restricted_list_title" = "Restricted users";
"lng_banned_list_title" = "Banned users";
"lng_admin_log_title_all" = "All actions";
"lng_admin_log_title_selected" = "Selected actions";
"lng_admin_log_filter" = "Filter";
"lng_admin_log_filter_title" = "Filter";
"lng_admin_log_filter_all_actions" = "All actions";
"lng_admin_log_filter_restrictions" = "New restrictions";
"lng_admin_log_filter_admins_new" = "New admins";
"lng_admin_log_filter_members_new" = "New members";
"lng_admin_log_filter_info_group" = "Group info";
"lng_admin_log_filter_info_channel" = "Channel info";
"lng_admin_log_filter_messages_deleted" = "Deleted messages";
"lng_admin_log_filter_messages_edited" = "Edited messages";
"lng_admin_log_filter_messages_pinned" = "Pinned messages";
"lng_admin_log_filter_members_removed" = "Leaving members";
"lng_admin_log_filter_all_admins" = "All users and admins";
"lng_admin_log_about" = "What is this?";
"lng_admin_log_about_text" = "This is a list of all service actions taken by the group's members and admins in the last 48 hours.";
"lng_admin_log_no_results_title" = "No actions found";
"lng_admin_log_no_results_text" = "No recent actions that match your query have been found.";
"lng_admin_log_no_results_search_text" = "No recent actions that contain '{query}' have been found.";
"lng_admin_log_no_events_title" = "No actions yet";
"lng_admin_log_no_events_text" = "There were no service actions\ntaken by the group's members\nand admins in the last 48 hours.";
"lng_admin_log_empty_text" = "Empty";
"lng_admin_log_changed_title_group" = "{from} changed group name to «{title}»";
"lng_admin_log_changed_title_channel" = "{from} changed channel name to «{title}»";
"lng_admin_log_changed_description_group" = "{from} edited group description:";
"lng_admin_log_removed_description_group" = "{from} removed group description";
"lng_admin_log_changed_description_channel" = "{from} edited channel description:";
"lng_admin_log_removed_description_channel" = "{from} removed channel description";
"lng_admin_log_previous_description" = "Previous description";
"lng_admin_log_changed_link_group" = "{from} changed group link:";
"lng_admin_log_removed_link_group" = "{from} removed group link";
"lng_admin_log_changed_link_channel" = "{from} changed channel link:";
"lng_admin_log_removed_link_channel" = "{from} removed channel link";
"lng_admin_log_previous_link" = "Previous link";
"lng_admin_log_changed_photo_group" = "{from} changed group photo";
"lng_admin_log_changed_photo_channel" = "{from} changed channel photo";
"lng_admin_log_removed_photo_group" = "{from} removed group photo";
"lng_admin_log_removed_photo_channel" = "{from} removed channel photo";
"lng_admin_log_invites_enabled" = "{from} enabled group invites";
"lng_admin_log_invites_disabled" = "{from} disabled group invites";
"lng_admin_log_signatures_enabled" = "{from} enabled signatures";
"lng_admin_log_signatures_disabled" = "{from} disabled signatures";
"lng_admin_log_pinned_message" = "{from} pinned message:";
"lng_admin_log_unpinned_message" = "{from} unpinned message";
"lng_admin_log_edited_caption" = "{from} edited caption:";
"lng_admin_log_removed_caption" = "{from} removed caption";
"lng_admin_log_previous_caption" = "Original caption";
"lng_admin_log_edited_message" = "{from} edited message:";
"lng_admin_log_previous_message" = "Original message";
"lng_admin_log_deleted_message" = "{from} deleted message:";
"lng_admin_log_participant_joined" = "{from} joined the group";
"lng_admin_log_participant_joined_channel" = "{from} joined the channel";
"lng_admin_log_participant_left" = "{from} left the group";
"lng_admin_log_participant_left_channel" = "{from} left the channel";
"lng_admin_log_invited" = "invited {user}";
"lng_admin_log_banned" = "banned {user}";
"lng_admin_log_restricted" = "changed restrictions for {user} {until}";
"lng_admin_log_promoted" = "changed privileges for {user}";
"lng_admin_log_user_with_username" = "{name} ({mention})";
"lng_admin_log_restricted_forever" = "indefinitely";
"lng_admin_log_restricted_until" = "until {date}";
"lng_admin_log_banned_view_messages" = "Read messages";
"lng_admin_log_banned_send_messages" = "Send messages";
"lng_admin_log_banned_send_media" = "Send media";
"lng_admin_log_banned_send_stickers" = "Send stickers & GIFs";
"lng_admin_log_banned_embed_links" = "Embed links";
"lng_admin_log_admin_change_info" = "Change info";
"lng_admin_log_admin_post_messages" = "Post messages";
"lng_admin_log_admin_edit_messages" = "Edit messages";
"lng_admin_log_admin_delete_messages" = "Delete messages";
"lng_admin_log_admin_ban_users" = "Ban users";
"lng_admin_log_admin_invite_users" = "Add users";
"lng_admin_log_admin_invite_link" = "Invite users via link";
"lng_admin_log_admin_pin_messages" = "Pin messages";
"lng_admin_log_admin_add_admins" = "Add new admins";
// Not used
"lng_topbar_info" = "Info";
"lng_profile_group_info" = "Group info";
"lng_profile_channel_info" = "Channel info";
"lng_channel_add_admins" = "New administrator";
// Wnd specific

View File

@@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_menu_update" = "Update";
"lng_menu_restart" = "Neustart";
"lng_menu_back" = "Zurück";
"lng_menu_night_mode" = "Nachtmodus";
"lng_disable_notifications_from_tray" = "Benachrichtigungen aus";
"lng_enable_notifications_from_tray" = "Benachrichtigungen ein";
@@ -88,7 +89,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_continue" = "Weiter";
"lng_close" = "Schließen";
"lng_connecting" = "Verbinde...";
"lng_reconnecting" = "Neu verbinden {count:jetzt|in # s|in # s}...";
"lng_connecting_to_proxy" = "Verbinde mit Proxy...";
"lng_connecting_settings" = "Einstellungen";
"lng_reconnecting#one" = "Neu verbinden in {count} s...";
"lng_reconnecting#other" = "Neu verbinden in {count} s...";
"lng_reconnecting_try_now" = "Jetzt versuchen";
"lng_status_service_notifications" = "Servicemeldungen";
@@ -102,8 +106,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_last_month" = "innerhalb eines Monats gesehen";
"lng_status_invisible" = "unsichtbar";
"lng_status_lastseen_now" = "gerade eben gesehen";
"lng_status_lastseen_minutes" = "zuletzt gesehen vor {count:_not_used_|# Minute|# Minuten}";
"lng_status_lastseen_hours" = "zuletzt gesehen vor {count:_not_used_|# Stunde|# Stunden}";
"lng_status_lastseen_minutes#one" = "zuletzt gesehen vor {count} Minute";
"lng_status_lastseen_minutes#other" = "zuletzt gesehen vor {count} Minuten";
"lng_status_lastseen_hours#one" = "zuletzt gesehen vor {count} Stunde";
"lng_status_lastseen_hours#other" = "zuletzt gesehen vor {count} Stunden";
"lng_status_lastseen_today" = "zuletzt heute um {time} gesehen";
"lng_status_lastseen_yesterday" = "gestern um {time} zuletzt gesehen";
"lng_status_lastseen_date" = "zuletzt gesehen am {date}";
@@ -112,24 +118,43 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_connecting" = "verbinden...";
"lng_chat_status_unaccessible" = "Gruppe ist nicht verfügbar";
"lng_chat_status_members" = "{count:keine Mitglieder|# Mitglied|# Mitglieder}";
"lng_chat_status_members_online" = "{count:_not_used_|# Mitglied|# Mitglieder}, {count_online:_not_used_|# online|# online}";
"lng_chat_status_no_members" = "keine Mitglieder";
"lng_chat_status_members#one" = "{count} Mitglied";
"lng_chat_status_members#other" = "{count} Mitglieder";
"lng_chat_status_online#one" = "{count} online";
"lng_chat_status_online#other" = "{count} online";
"lng_chat_status_members_online" = "{members_count}, {online_count}";
"lng_channel_status" = "Kanal";
"lng_group_status" = "Gruppe";
"lng_channel_members_link" = "{count:_not_used_|# Mitglied|# Mitglieder}";
"lng_channel_admins_link" = "{count:_not_used_|# Administrator|# Administratoren}";
"lng_channel_members_link#one" = "{count} Mitglied";
"lng_channel_members_link#other" = "{count} Mitglieder";
"lng_channel_admins_link#one" = "{count} Administrator";
"lng_channel_admins_link#other" = "{count} Administratoren";
"lng_server_error" = "Interner Serverfehler.";
"lng_flood_error" = "Zu viele Versuche, bitte später erneut probieren.";
"lng_gif_error" = "GIF-Animation kann nicht abgespielt werden :(";
"lng_gif_error" = "Ein Fehler ist beim Laden der GIF-Animation aufgetreten :(";
"lng_edit_error" = "Du kannst diese Nachricht nicht bearbeiten";
"lng_join_channel_error" = "Du bist bereits in zu vielen Kanälen und Supergruppen. Bitte trenne dich zuvor von ein paar davon.";
"lng_error_phone_flood" = "Du hast dein Konto leider zu oft gelöscht. Bitte warte einige Tage, erst dann kannst du dich erneut registrieren.";
"lng_error_start_minimized_passcoded" = "Du hast den lokalen Pincode-Schutz aktiviert, deshalb kannst du die App nicht minimiert starten. Die App muss dich vorher nach dem Pincode fragen.";
"lng_error_pinned_max" = "Du kannst nicht mehr als {count:_not_used_|# Chat|# Chats} oben anheften.";
"lng_error_pinned_max#one" = "Du kannst nicht mehr als {count} Chat oben anheften.";
"lng_error_pinned_max#other" = "Du kannst nicht mehr als {count} Chats oben anheften.";
"lng_error_public_groups_denied" = "Leider kannst du an keinen öffentlichen Gruppen mehr teilnehmen.\n{more_info}";
"lng_error_cant_edit_admin" = "Du kannst die Berechtigungen für diesen Admin nicht ändern.";
"lng_error_cant_add_member" = "Du kannst diesen Bot leider nicht der Gruppe hinzufügen. Ein Admin der Gruppe kann ihn hinzufügen.";
"lng_error_cant_add_bot" = "Dieser Bot kann Gruppen nicht hinzugefügt werden.";
"lng_error_cant_add_admin_invite" = "Du kannst diesen Nutzer nicht als Admin festlegen, da er kein Mitglied dieser Gruppe ist oder du keine Erlaubnis hast, ihn einzuladen.";
"lng_error_cant_add_admin_unban" = "Du kannst diesen Nutzer nicht als Admin festlegen, da er sich in der Sperrliste befindet und du ihn nicht entsperren darfst.";
"lng_error_cant_ban_admin" = "Du kannst diesen Nutzer nicht sperren, da er ein Admin dieser Gruppe ist und du keine Berechtigung hast, ihn zurückzustufen.";
"lng_sure_add_admin_invite" = "Dieser Nutzer ist kein Gruppenmitglied. Möchtest du ihn der Gruppe hinzufügen und als Admin festlegen?";
"lng_sure_add_admin_unban" = "Dieser Nutzer ist derzeit in dieser Gruppe eingeschränkt oder gesperrt. Möchtest du ihn entsperren und befördern?";
"lng_sure_ban_admin" = "Dieser Nutzer ist Admin in dieser Gruppe. Möchtest du fortfahren und ihn einschränken?";
"lng_sure_ban_user_group" = "{user} in der Gruppe sperren?";
"lng_sure_enable_socks" = "Möchtest du wirklich diesen Proxy aktivieren?\n\nServer: {server}\nPort: {port}\n\nDu kannst ihn später in den Einstellungen (Verbindungsart) ändern.";
"lng_sure_enable" = "Aktivieren";
"lng_edit_deleted" = "Diese Nachricht wurde gelöscht";
"lng_edit_too_long" = "Der Text ist leider zu lang";
@@ -198,9 +223,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_signin_sure_reset" = "Hinweis!\n\nDu verlierst du alle Chats und Nachrichten, ebenso deine geteilten Bilder und Videos.\n\nKonto wirklich zurücksetzen?";
"lng_signin_reset" = "Zurücksetzen";
"lng_signin_reset_wait" = "Da dein Konto {phone_number} aktiv und durch ein Kennwort geschützt ist, löschen wir es aus Sicherheitsgründen in einer Woche. Du kannst den Vorgang jederzeit abbrechen.\n\nDu kannst dein Konto zurücksetzen in:\n{when}";
"lng_signin_reset_in_days" = "{count_days:0 Tage|# Tag|# Tage} {count_hours:0 Stunden|# Stunde|# Stunden} {count_minutes:0 Minuten|# Minute|# Minuten}";
"lng_signin_reset_in_hours" = "{count_hours:0 Stunden|# Stunde|# Stunden} {count_minutes:0 Minuten|# Minute|# Minuten}";
"lng_signin_reset_in_minutes" = "{count_minutes:0 Minuten|# Minute|# Minuten}";
"lng_signin_reset_days#one" = "{count} Tag";
"lng_signin_reset_days#other" = "{count} Tage";
"lng_signin_reset_hours#one" = "{count} Stunde";
"lng_signin_reset_hours#other" = "{count} Stunden";
"lng_signin_reset_minutes#one" = "{count} Minute";
"lng_signin_reset_minutes#other" = "{count} Minuten";
"lng_signin_reset_in_days" = "{days_count} {hours_count} {minutes_count}";
"lng_signin_reset_in_hours" = "{hours_count} {minutes_count}";
"lng_signin_reset_cancelled" = "Deine vorherigen Versuche das Konto zurückzusetzen wurden durch den aktiven Nutzer abgebrochen. Bitte in 7 Tagen erneut probieren.";
"lng_signup_title" = "Deine Info";
@@ -223,7 +253,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_settings_save" = "Speichern";
"lng_settings_upload" = "Profilbild festlegen";
"lng_settings_crop_profile" = "Sichtbaren Bereich für Bild wählen";
"lng_settings_crop_profile" = "Wähle einen Bereich für dein Profilbild";
"lng_settings_uploading_photo" = "Bild wird geladen...";
"lng_settings_edit" = "Bearbeiten";
"lng_settings_drop_area_subtitle" = "um es als dein Bild auszuwählen";
@@ -241,10 +271,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_username_link" = "Dieser Link öffnet einen Chat mit dir:";
"lng_username_copied" = "Link in die Zwischenablage kopiert.";
"lng_bio_title" = "Bio bearbeiten";
"lng_bio_placeholder" = "Bio";
"lng_bio_about" = "Hier kannst du etwas über dich schreiben. Jeder der dein Profil öffnet, kann diesen Text lesen.";
"lng_settings_section_info" = "Info";
"lng_settings_phone_number" = "Telefonnummer:";
"lng_settings_username" = "Benutzername:";
"lng_settings_choose_username" = "Benutzername wählen";
"lng_settings_empty_bio" = "Keine";
"lng_settings_section_notify" = "Benachrichtigungen";
"lng_settings_desktop_notify" = "Desktopbenachrichtigungen";
@@ -303,7 +338,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_backgrounds_header" = "Neuen Chat-Hintergrund wählen";
"lng_theme_sure_keep" = "Wirklich dieses Thema verwenden?";
"lng_theme_reverting" = "Vorheriges Thema wird in {count:_not_used_|# Sekunde|# Sekunden} benutzt.";
"lng_theme_reverting#one" = "Vorheriges Thema wird in {count} Sekunde benutzt.";
"lng_theme_reverting#other" = "Vorheriges Thema wird in {count} Sekunden benutzt.";
"lng_theme_keep_changes" = "Anwenden";
"lng_theme_revert" = "Zurücksetzen";
@@ -329,14 +365,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_local_storage_title" = "Lokaler Cache";
"lng_settings_no_data_cached" = "Keine temporären Daten!";
"lng_settings_images_cached" = "{count:_not_used_|# Bild|# Bilder}, {size}";
"lng_settings_audios_cached" = "{count:_not_used_|# Sprachnachricht|# Sprachnachrichten}, {size}";
"lng_settings_images_cached#one" = "{count} Bild, {size}";
"lng_settings_images_cached#other" = "{count} Bilder, {size}";
"lng_settings_audios_cached#one" = "{count} Sprachnachricht, {size}";
"lng_settings_audios_cached#other" = "{count} Sprachnachrichten, {size}";
"lng_local_storage_clear" = "Leeren";
"lng_local_storage_clearing" = "Leeren...";
"lng_local_storage_cleared" = "Alles entfernt!";
"lng_local_storage_clear_failed" = "Ein Fehler ist aufgetreten :(";
"lng_settings_section_advanced_settings" = "Erweiterte Einstellungen";
"lng_settings_enable_night_theme" = "Nachtmodus aktivieren";
"lng_settings_disable_night_theme" = "Nachtmodus deaktivieren";
"lng_passcode_remove_button" = "Entfernen";
@@ -348,8 +388,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_passcode_autolock" = "Auto-Sperre";
"lng_passcode_autolock_away" = "Auto-Sperre bei Abwesenheit:";
"lng_passcode_autolock_inactive" = "Auto-Sperre bei Inaktivität:";
"lng_passcode_autolock_minutes" = "{count:_not_used_|# Minute|# Minuten}";
"lng_passcode_autolock_hours" = "{count:_not_used_|# Stunde|# Stunden}";
"lng_passcode_autolock_minutes#one" = "{count} Minute";
"lng_passcode_autolock_minutes#other" = "{count} Minuten";
"lng_passcode_autolock_hours#one" = "{count} Stunde";
"lng_passcode_autolock_hours#other" = "{count} Stunden";
"lng_passcode_enter_old" = "Aktuellen Pincode eingeben";
"lng_passcode_enter_first" = "Pincode festlegen";
"lng_passcode_enter_new" = "Neuer Pincode";
@@ -455,32 +497,46 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_privacy_lastseen_title" = "Zuletzt gesehen";
"lng_edit_privacy_lastseen_description" = "Wer darf deine \"zuletzt gesehen\" Zeit sehen:";
"lng_edit_privacy_lastseen_warning" = "Wichtig: Du kannst den \"zuletzt gesehen\" Status nur von Personen sehen, mit denen du auch deinen teilst. Sonst wird nur die ungefähre Zeit angezeigt.";
"lng_edit_privacy_lastseen_always" = "Immer teilen mit{count:| # Kontakt| # Kontakten}";
"lng_edit_privacy_lastseen_never" = "Niemals teilen mit{count:| # Kontakt| # Kontakten}";
"lng_edit_privacy_lastseen_always_empty" = "Immer teilen mit";
"lng_edit_privacy_lastseen_always#one" = "Immer teilen mit {count} Kontakt";
"lng_edit_privacy_lastseen_always#other" = "Immer teilen mit {count} Kontakten";
"lng_edit_privacy_lastseen_never_empty" = "Niemals teilen mit";
"lng_edit_privacy_lastseen_never#one" = "Niemals teilen mit {count} Kontakt";
"lng_edit_privacy_lastseen_never#other" = "Niemals teilen mit {count} Kontakten";
"lng_edit_privacy_lastseen_exceptions" = "Hier kannst du Kontakte hinzufügen, für die eine Ausnahme gemacht werden soll.";
"lng_edit_privacy_lastseen_always_title" = "Immer teilen mit";
"lng_edit_privacy_lastseen_never_title" = "Niemals teilen mit";
"lng_edit_privacy_groups_title" = "Wer kann mich Gruppen hinzufügen?";
"lng_edit_privacy_groups_description" = "Du kannst bestimmen, wer dich in Gruppen oder Kanälen hinzufügen kann:";
"lng_edit_privacy_groups_always" = "Immer erlauben{count:| # Kontakt| # Kontakten}";
"lng_edit_privacy_groups_never" = "Niemals erlauben{count:| # Kontakt| # Kontakten}";
"lng_edit_privacy_groups_always_empty" = "Immer erlauben";
"lng_edit_privacy_groups_always#one" = "Immer {count} Kontakt erlauben";
"lng_edit_privacy_groups_always#other" = "Immer {count} Kontakten erlauben";
"lng_edit_privacy_groups_never_empty" = "Niemals erlauben";
"lng_edit_privacy_groups_never#one" = "Niemals {count} Kontakt erlauben";
"lng_edit_privacy_groups_never#other" = "Niemals {count} Kontakten erlauben";
"lng_edit_privacy_groups_exceptions" = "Hier kannst du Nutzer hinzufügen, für die eine Ausnahme gemacht werden soll.";
"lng_edit_privacy_groups_always_title" = "Immer erlauben";
"lng_edit_privacy_groups_never_title" = "Niemals erlauben";
"lng_edit_privacy_calls_title" = "Anrufe";
"lng_edit_privacy_calls_description" = "Du kannst festlegen, wer dich anrufen darf:";
"lng_edit_privacy_calls_always" = "Immer erlauben{count:| # Kontakt| # Kontakte}";
"lng_edit_privacy_calls_never" = "Niemals erlauben{count:| # Kontakt| # Kontakte}";
"lng_edit_privacy_calls_always_empty" = "Immer erlauben";
"lng_edit_privacy_calls_always#one" = "Immer {count} Kontakt erlauben";
"lng_edit_privacy_calls_always#other" = "Immer {count} Kontakten erlauben";
"lng_edit_privacy_calls_never_empty" = "Niemals erlauben";
"lng_edit_privacy_calls_never#one" = "Niemals {count} Kontakt erlauben";
"lng_edit_privacy_calls_never#other" = "Niemals {count} Kontakten erlauben";
"lng_edit_privacy_calls_exceptions" = "Hier kannst du Nutzer hinzufügen, für die eine Ausnahme gemacht werden soll.";
"lng_edit_privacy_calls_always_title" = "Immer erlauben";
"lng_edit_privacy_calls_never_title" = "Niemals erlauben";
"lng_self_destruct_title" = "Automatische Kontolöschung";
"lng_self_destruct_description" = "Wenn du dich innerhalb dieser Zeit nicht anmeldest, wird dein Konto mit allen Nachrichten, Gruppen und Kontakten gelöscht.";
"lng_self_destruct_months" = "{count:_not_used_|# Monat|# Monate}";
"lng_self_destruct_years" = "{count:_not_used_|# Jahr|# Jahre}";
"lng_self_destruct_months#one" = "{count} Monat";
"lng_self_destruct_months#other" = "{count} Monate";
"lng_self_destruct_years#one" = "{count} Jahr";
"lng_self_destruct_years#other" = "{count} Jahre";
"lng_change_phone_title" = "Nummer ändern";
"lng_change_phone_description" = "Du kannst deine Telefonnummer hier ändern.\nDein Konto und alle Daten in der Cloud, also \nNachrichten, Medien, Kontakte, etc. werden \nauf das neue Konto übertragen.\n\n[b]Wichtig[/b]: Alle deine Kontakte erhalten deine\n[b]neue Nummer[/b] ihrem Telefonbuch\nhinzugefügt, sofern sie deine alte Nummer \nhatten und du sie nicht blockiert hattest.";
@@ -506,15 +562,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_invite_link_section" = "Einladungslink";
"lng_profile_create_public_link" = "Öffentlichen Link erstellen";
"lng_profile_edit_public_link" = "Öffentlichen Link bearbeiten";
"lng_profile_search_members" = "Mitglieder suchen";
"lng_profile_manage_admins" = "Administratoren verwalten";
"lng_profile_manage_blocklist" = "Blockierte Nutzer verwalten";
"lng_profile_common_groups" = "{count:_not_used_|# gemeinsame Gruppe|# gemeinsame Gruppen}";
"lng_profile_manage_blocklist" = "Gesperrte Nutzer verwalten";
"lng_profile_manage_restrictedlist" = "Eingeschränke Nutzer verwalten";
"lng_profile_recent_actions" = "Letzte Aktionen";
"lng_profile_common_groups#one" = "{count} gemeinsame Gruppe";
"lng_profile_common_groups#other" = "{count} gemeinsame Gruppen";
"lng_profile_common_groups_section" = "Gemeinsame Gruppen";
"lng_profile_participants_section" = "Teilnehmer";
"lng_profile_info_section" = "Info";
"lng_profile_mobile_number" = "Telefon:";
"lng_profile_username" = "Benutzername:";
"lng_profile_link" = "Link:";
"lng_profile_bio" = "Bio:";
"lng_profile_add_contact" = "Kontakt hinzufügen";
"lng_profile_edit_contact" = "Bearbeiten";
"lng_profile_enable_notifications" = "Benachrichtigungen";
@@ -542,23 +603,30 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_delete_and_exit" = "Verlassen";
"lng_profile_kick" = "Entfernen";
"lng_profile_admin" = "Admin";
"lng_profile_edit_permissions" = "Bearbeiten";
"lng_profile_sure_kick" = "{user} aus der Gruppe entfernen?";
"lng_profile_sure_kick_channel" = "{user} aus dem Kanal entfernen?";
"lng_profile_sure_kick_admin" = "{user} als Administrator entfernen?";
"lng_profile_loading" = "Lade...";
"lng_profile_shared_media" = "Geteilte Medien";
"lng_profile_no_media" = "Noch keine Medien in diesem Chat";
"lng_profile_photos" = "{count:_not_used_|# Bild|# Bilder}";
"lng_profile_photos#one" = "{count} Bild";
"lng_profile_photos#other" = "{count} Bilder";
"lng_profile_photos_header" = "Bilder";
"lng_profile_videos" = "{count:_not_used_|# Video|# Videos}";
"lng_profile_videos#one" = "{count} Video";
"lng_profile_videos#other" = "{count} Videos";
"lng_profile_videos_header" = "Videos";
"lng_profile_songs" = "{count:_not_used_|# Audio|# Audios}";
"lng_profile_songs#one" = "{count} Audiodatei";
"lng_profile_songs#other" = "{count} Audiodateien";
"lng_profile_songs_header" = "Audiodateien";
"lng_profile_files" = "{count:_not_used_|# Datei|# Dateien}";
"lng_profile_files#one" = "{count} Datei";
"lng_profile_files#other" = "{count} Dateien";
"lng_profile_files_header" = "Dateien";
"lng_profile_audios" = "{count:_not_used_|# Sprachnachricht|# Sprachnachrichten}";
"lng_profile_audios#one" = "{count} Sprachnachricht";
"lng_profile_audios#other" = "{count} Sprachnachrichten";
"lng_profile_audios_header" = "Sprachnachrichten";
"lng_profile_shared_links" = "{count:_not_used_|# Geteilter Link|# Geteilte Links}";
"lng_profile_shared_links#one" = "{count} geteilter Link";
"lng_profile_shared_links#other" = "{count} geteilte Links";
"lng_profile_shared_links_header" = "Geteilte Links";
"lng_profile_copy_phone" = "Telefonnummer kopieren";
"lng_profile_copy_fullname" = "Name kopieren";
@@ -578,16 +646,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_report_button" = "Melden";
"lng_report_thanks" = "Vielen Dank! Unser Team wird sich um deine Meldung kümmern.";
"lng_channel_add_admins" = "Neuer Administrator";
"lng_channel_add_members" = "Mitglieder hinzufügen";
"lng_channel_add_banned" = "Nutzer sperren";
"lng_channel_add_restricted" = "Nutzer einschränken";
"lng_channel_members" = "Mitglieder";
"lng_channel_only_last_shown" = "Nur {count:_not_used_|# letztes Mitglied wird angezeigt|letzte # Mitglieder werden angezeigt}";
"lng_channel_only_last_shown#one" = "Nur {count} letztes Mitgled wird hier angezeigt";
"lng_channel_only_last_shown#other" = "Nur {count} letzte Mitgleder werden hier angezeigt";
"lng_channel_admins" = "Administratoren";
"lng_channel_add_admin" = "Administrator hinzufügen";
"lng_channel_admin_sure" = "Soll {user} Administrator werden?";
"lng_channel_admins_too_much" = "Leider kannst du keine weiteren Admins dieser Gruppe hinzufügen, bitte erst welche löschen.";
"lng_channel_admin_status_creator" = "Ersteller";
"lng_channel_admin_status_promoted_by" = "Befördert von {user}";
"lng_channel_admin_status_not_admin" = "Kein Administrator";
"lng_group_blocked_list_about" = "Blockierte Nutzer werden aus der Gruppe entfernt und können nur durch einen Admin hinzugefügt werden.\n\nEinladungslinks funktionieren nicht.";
"lng_group_blocked_list_about" = "Gesperrte Nutzer werden von der Gruppe entfernt und können nur durch Admins erneut hinzugefügt werden. \nEinladungslinks funktionieren nicht.";
"lng_chat_all_members_admins" = "Alle Mitglieder Sind Admins";
"lng_chat_about_all_admins" = "Gruppenmitglieder können neue Leute hinzufügen sowie den Gruppennamen und das Bild ändern.";
@@ -595,7 +667,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_participant_filter" = "Suche";
"lng_participant_invite" = "Einladen";
"lng_participant_invite_sorry" = "Du kannst selbst nur {count:_not_used| das erste Mitglied|die ersten # Mitglieder} in einen Kanal einladen.\n\nWeitere Mitglieder können deinem Kanal über den Einladungslink beitreten.";
"lng_participant_invite_sorry#one" = "Du kannst selbst nur das erste {count} Mitglied einem Kanal hinzufügen.\n\nWeitere Leute können den Kanal über den Einladungslink betreten.";
"lng_participant_invite_sorry#other" = "Du kannst selbst nur die ersten {count} Mitglieder einem Kanal hinzufügen.\n\nWeitere Leute können den Kanal über den Einladungslink betreten.";
"lng_create_group_back" = "Zurück";
"lng_create_group_next" = "Weiter";
"lng_create_group_create" = "Erstellen";
@@ -618,8 +691,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_create_channel_link_available" = "Dieser Link ist verfügbar";
"lng_create_channel_link_copied" = "Link in die Zwischenablage kopiert";
"lng_create_group_crop" = "Sichtbaren Bereich für Bild wählen";
"lng_create_channel_crop" = "Sichtbaren Bereich für Bild wählen";
"lng_create_group_crop" = "Wähle einen Bereich für dein Gruppenbild";
"lng_create_channel_crop" = "Wähle einen Bereich für dein Kanalbild";
"lng_failed_add_participant" = "Kann Teilnehmer nicht hinzufügen. Später erneut versuchen.";
"lng_failed_add_not_mutual" = "Wenn man eine Gruppe verlässt, kann nur ein gemeinsamer Kontakt die Person erneut einladen (beide Seiten müssen die Nummer des anderen gespeichert haben).";
@@ -637,8 +710,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_message_empty" = "Leere Nachricht";
"lng_message_unsupported" = "Diese Nachricht wird von deiner Telegram Desktop Version nicht unterstützt. Bitte aktualisiere sie in den Einstellungen oder über {link}";
"lng_duration_seconds" = "{count:_not_used_|# Sekunde|# Sekunden}";
"lng_duration_minutes_seconds" = "{count_minutes:# Min|# Min|# Min} {count_seconds:# Sek|# Sek|# Sek}";
"lng_duration_seconds#one" = "{count} Sekunde";
"lng_duration_seconds#other" = "{count} Sekunden";
"lng_duration_minsec_minutes#one" = "{count} Min";
"lng_duration_minsec_minutes#other" = "{count} Min";
"lng_duration_minsec_seconds#one" = "{count} Sek";
"lng_duration_minsec_seconds#other" = "{count} Sek";
"lng_duration_minutes_seconds" = "{minutes_count} {seconds_count}";
"lng_action_add_user" = "{from} hat {user} hinzugefügt";
"lng_action_add_users_many" = "{from} hat {users} hinzugefügt";
@@ -676,18 +754,33 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_action_pinned_media_sticker" = "einen Sticker";
"lng_action_pinned_media_emoji_sticker" = "einen {emoji} Sticker";
"lng_action_pinned_media_game" = "das Spiel «{game}»";
"lng_action_game_score" = "{from} erzielte {count:# Punkte|# Punkt|# Punkte} bei {game}";
"lng_action_game_you_scored" = "Du hast {count:# Punkte|# Punkt|# Punkte} bei {game} erzielt";
"lng_action_game_score_no_game" = "{from} erzielte {count:# Punkte|# Punkt|# Punkte}";
"lng_action_game_you_scored_no_game" = "Du hast {count:# Punkte erzielt|# Punkt erzielt|# Punkte erzielt}";
"lng_action_game_score#one" = "{from} erzielte {count} Punkt bei {game}";
"lng_action_game_score#other" = "{from} erzielte {count} Punkte bei {game}";
"lng_action_game_you_scored#one" = "Du hast {count} Punkt bei {game} erzielt";
"lng_action_game_you_scored#other" = "Du hast {count} Punkte bei {game} erzielt";
"lng_action_game_score_no_game#one" = "{from} erzielte {count} Punkt";
"lng_action_game_score_no_game#other" = "{from} erzielte {count} Punkte";
"lng_action_game_you_scored_no_game#one" = "Du hast {count} Punkt erzielt";
"lng_action_game_you_scored_no_game#other" = "Du hast {count} Punkte erzielt";
"lng_action_payment_done" = "Du hast erfolgreich {amount} an {user} übermittelt";
"lng_action_payment_done_for" = "Du hast erfolgreich {amount} an {user} für {invoice} übermittelt";
"lng_action_took_screenshot" = "{from} hat ein Bildschirmfoto gemacht!";
"lng_action_you_took_screenshot" = "Du hast ein Bildschirmfoto gemacht!";
"lng_profile_migrate_reached" = "Limit von {count:_not_used_|# Mitglied|# Mitgliedern} erreicht";
"lng_ttl_photo_received" = "{from} hat dir ein selbstzerstörendes Bild gesendet. Um es anzuzeigen, bitte Handy benutzen.";
"lng_ttl_photo_sent" = "Du hast ein selbstzerstörendes Bild gesendet.";
"lng_ttl_photo_expired" = "Bild ist abgelaufen";
"lng_ttl_video_received" = "{from} hat dir ein selbstzerstörendes Video gesendet. Um es anzuzeigen, bitte Handy benutzen.";
"lng_ttl_video_sent" = "Du hast ein selbstzerstörendes Video gesendet.";
"lng_ttl_video_expired" = "Video ist abgelaufen";
"lng_profile_migrate_reached#one" = "Limit von {count} Mitglied erreicht";
"lng_profile_migrate_reached#other" = "Limit von {count} Mitgliedern erreicht";
"lng_profile_migrate_body" = "Um das Limit aufzuheben, ändere die Gruppe in eine Supergruppe.";
"lng_profile_migrate_learn_more" = "Mehr unter:";
"lng_profile_migrate_about" = "Für weitere Funktionen und um das Limit aufzuheben in Supergruppe ändern:";
"lng_profile_migrate_feature1" = "— Bis zu {count:_not_used_|# Mitglied|# Mitglieder} sind nun möglich";
"lng_profile_migrate_feature1#one" = "— Das Limit beträgt {count} Mitglied";
"lng_profile_migrate_feature1#other" = "— Das Limit beträgt {count} Mitglieder";
"lng_profile_migrate_feature2" = "— Neue Mitglieder sehen gesamten Verlauf";
"lng_profile_migrate_feature3" = "— Admins können alle Nachrichten löschen";
"lng_profile_migrate_feature4" = "— Mitteilungen sind standardmäßig stumm";
@@ -702,7 +795,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_convert_feature4" = "— Gründer kann Gruppe öffentlich machen";
"lng_profile_convert_warning" = "{bold_start}Wichtig:{bold_end} Die Änderung in eine Supergruppe kann nicht rückgängig gemacht werden.";
"lng_profile_convert_confirm" = "Ändern";
"lng_profile_add_more_after_upgrade" = "Du kannst nach der Änderung der Gruppe in eine Supergruppe bis zu {count:_not_used_|# Mitglied|# Mitglieder} hinzufügen.";
"lng_profile_add_more_after_upgrade#one" = "Du kannst nach der Änderung der Gruppe in eine Supergruppe bis zu {count} Mitglied hinzufügen.";
"lng_profile_add_more_after_upgrade#other" = "Du kannst nach der Änderung der Gruppe in eine Supergruppe bis zu {count} Mitglieder hinzufügen.";
"lng_channel_not_accessible" = "Dieser Kanal ist nicht zugänglich";
"lng_group_not_accessible" = "Diese Gruppe ist nicht zugänglich.";
@@ -717,7 +811,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_group_invite_want_join_channel" = "Möchtest du dem Kanal «{title}» beitreten?";
"lng_group_invite_join" = "Beitreten";
"lng_group_invite_members" = "{count:_not_used_|# Mitglied|# Mitglieder}, darunter:";
"lng_group_invite_members#one" = "{count} Mitglied, darunter:";
"lng_group_invite_members#other" = "{count} Mitglieder, darunter:";
"lng_group_invite_link" = "Einladungslink:";
"lng_group_invite_create" = "Neuer Link";
@@ -730,6 +825,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_channel_public_link_copied" = "Link in die Zwischenablage kopiert.\n";
"lng_forwarded" = "Weitergeleitet von {user}";
"lng_forwarded_date" = "Original: {date}";
"lng_forwarded_channel" = "Weitergeleitet aus {channel}";
"lng_forwarded_via" = "Weitergeleitet von {user} via {inline_bot}";
"lng_forwarded_channel_via" = "Weitergeleitet aus {channel} via {inline_bot}";
@@ -809,7 +905,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_stickers_you_have" = "Sticker-Pakete verwalten";
"lng_stickers_featured" = "Angesagte Sticker";
"lng_stickers_return" = "Rückgängig";
"lng_stickers_count" = "{count:Lade...|# Sticker|# Sticker}";
"lng_stickers_count#one" = "{count} Sticker";
"lng_stickers_count#other" = "{count} Sticker";
"lng_stickers_masks_pack" = "Das ist ein Masken-Paket. Du kannst Masken nur im Editor unserer mobilen Apps verwenden.";
"lng_in_dlg_photo" = "Bild";
@@ -839,6 +936,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_cant_invite_banned" = "Nur Admins können diesen Nutzer hinzufügen.";
"lng_cant_invite_privacy" = "Du kannst mit diesem Nutzer keine Gruppe erstellen, weil er es nicht erlaubt.";
"lng_cant_invite_privacy_channel" = "Du kannst diesen Nutzer keinen Kanälen hinzufügen, weil er es nicht erlaubt.";
"lng_cant_delete_group#one" = "Über die App kannst du derzeit keine Gruppen mit mehr als {count} Mitglied löschen. Bitte melde dich bei unserem Support Chat, wenn du diese Gruppe löschen lassen möchtest.";
"lng_cant_delete_group#other" = "Über die App kannst du derzeit keine Gruppen mit mehr als {count} Mitgliedern löschen. Bitte melde dich bei unserem Support Chat, wenn du diese Gruppe löschen lassen möchtest.";
"lng_cant_delete_channel#one" = "Über die App kannst du derzeit keine Kanäle mit mehr als {count} Mitglied löschen. Bitte melde dich bei unserem Support Chat, wenn du diesen Kanal löschen lassen möchtest.";
"lng_cant_delete_channel#other" = "Über die App kannst du derzeit keinen Kanäle mit mehr als {count} Mitgliedern löschen. Bitte melde dich bei unserem Support Chat, wenn du diesen Kanal löschen lassen möchtest.";
"lng_cant_do_this" = "Verzeihung. Das ist leider nicht möglich.";
"lng_send_button" = "Senden";
@@ -884,11 +985,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_typing" = "tippt";
"lng_user_typing" = "{user} tippt";
"lng_users_typing" = "{user} und {second_user} tippen";
"lng_many_typing" = "{count:_not_used_|# tippt|# tippen}";
"lng_many_typing#one" = "{count} tippt";
"lng_many_typing#other" = "{count} tippen";
"lng_playing_game" = "spielt ein Spiel";
"lng_user_playing_game" = "{user} spielt ein Spiel";
"lng_users_playing_game" = "{user} und {second_user} spielen ein Spiel";
"lng_many_playing_game" = "{count:_not_used_|# spielt|# spielen} ein Spiel";
"lng_many_playing_game#one" = "{count} spielt ein Spiel";
"lng_many_playing_game#other" = "{count} spielen ein Spiel";
"lng_send_action_record_video" = "schickt Video";
"lng_user_action_record_video" = "{user} sendet ein Video";
"lng_send_action_upload_video" = "schickt ein Video";
@@ -909,7 +1012,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_user_action_geo_location" = "{user} wählt einen Standort aus";
"lng_send_action_choose_contact" = "wählt einen Kontakt aus";
"lng_user_action_choose_contact" = "{user} wählt einen Kontakt aus";
"lng_unread_bar" = "{count:_not_used_|# Ungelesene Nachricht|# Ungelesene Nachrichten}";
"lng_unread_bar#one" = "{count} ungelesene Nachricht";
"lng_unread_bar#other" = "{count} ungelesene Nachrichten";
"lng_maps_point" = "Standort";
"lng_save_photo" = "Bild speichern";
@@ -932,7 +1036,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_from_top" = "Oben entfernen";
"lng_context_promote_admin" = "Zum Admin machen";
"lng_context_remove_admin" = "Als Admin entfernen";
"lng_context_edit_permissions" = "Berechtigungen bearbeiten";
"lng_context_restrict_user" = "Nutzer einschränken";
"lng_context_remove_from_group" = "Aus Gruppe entfernen";
"lng_context_copy_link" = "Link kopieren";
@@ -970,31 +1075,41 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_msg" = "Nachricht entfernen";
"lng_context_cancel_upload" = "Upload abbrechen";
"lng_context_copy_selected" = "Text kopieren";
"lng_context_copy_selected_items" = "Als Text kopieren";
"lng_context_forward_selected" = "Auswahl weiterleiten";
"lng_context_delete_selected" = "Auswahl löschen";
"lng_context_clear_selection" = "Auswahl aufheben";
"lng_really_send_image" = "Willst du dieses Bild senden?";
"lng_really_send_file" = "Willst du diese Datei senden?";
"lng_really_share_contact" = "Willst du diesen Kontakt senden?";
"lng_send_images_compress" = "Komprimiere {count:_not_used_|Bild|Bilder}";
"lng_send_images_compress#one" = "Bild komprimieren";
"lng_send_images_compress#other" = "Bilder komprimieren";
"lng_send_image_non_local" = "{name} befindet sich nicht direkt auf dem Gerät";
"lng_send_image_empty" = "Leere Datei {name} konnte nicht gesendet werden";
"lng_send_image_too_large" = "{name} ist größer als 1500 MB und kann daher nicht gesendet werden";
"lng_send_folder" = "Verzeichnis «{name}» kann nicht gesendet werden :(";
"lng_send_images_selected" = "{count:_not_used_|# Bild|# Bilder} ausgewählt";
"lng_send_photos" = "{count:_not_used_|# Bild|# Bilder} senden";
"lng_send_files_selected" = "{count:_not_used_|# Datei|# Dateien} ausgewählt";
"lng_send_files" = "{count:_not_used_|# Datei|# Dateien} senden";
"lng_send_images_selected#one" = "{count} Bild ausgewählt";
"lng_send_images_selected#other" = "{count} Bilder ausgewählt";
"lng_send_photos#one" = "{count} Bild senden";
"lng_send_photos#other" = "{count} Bilder senden";
"lng_send_files_selected#one" = "{count} Datei ausgewählt";
"lng_send_files_selected#other" = "{count} Dateien ausgewählt";
"lng_send_files#one" = "{count} Datei senden";
"lng_send_files#other" = "{count} Dateien senden";
"lng_forward_choose" = "Empfänger wählen...";
"lng_forward_cant" = "Weiterleiten nicht möglich :(";
"lng_forward_confirm" = "An {recipient} weiterleiten?";
"lng_forward_share_contact" = "Kontakt an {recipient} senden?";
"lng_forward_share_cant" = "Kontakt teilen nicht möglich :(";
"lng_forward_send_file_confirm" = "«{name}» an {recipient} senden?";
"lng_forward_send_files_confirm" = "Ausgewählte Dateien an {recipient} senden?";
"lng_forward_send_files_cant" = "Medien senden nicht möglich :(";
"lng_forward_send" = "Senden";
"lng_forward_messages" = "{count:_not_used_|Nachrichtenanhang|# Nachrichtenanhänge}";
"lng_forwarding_from" = "{user} und {count:_not_used_|# anderer|# andere}";
"lng_forward_messages#one" = "{count} weitergeleitete Nachricht";
"lng_forward_messages#other" = "{count} weitergeleitete Nachrichten";
"lng_forwarding_from#one" = "{user} und {count} anderer";
"lng_forwarding_from#other" = "{user} und {count} andere";
"lng_forwarding_from_two" = "{user} und {second_user}";
"lng_inline_switch_choose" = "Chat wählen...";
"lng_inline_switch_cant" = "Hier kannst du nicht schreiben :(";
@@ -1016,6 +1131,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_contact_title" = "Kontaktnamen ändern";
"lng_edit_channel_title" = "Kanal bearbeiten";
"lng_edit_sign_messages" = "Nachrichten unterschreiben";
"lng_edit_group_who_invites" = "Wer kann Mitglieder hinzufügen";
"lng_edit_group_invites_everybody" = "Alle Mitglieder";
"lng_edit_group_invites_only_admins" = "Nur Admins";
"lng_edit_group" = "Gruppe bearbeiten";
"lng_edit_self_title" = "Name bearbeiten";
"lng_confirm_contact_data" = "Neuer Kontakt";
@@ -1040,15 +1158,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_selected_clear" = "Abbrechen";
"lng_selected_delete" = "Löschen";
"lng_selected_forward" = "Weiterleiten";
"lng_selected_count" = "{count:_not_used_|# Nachricht|# Nachrichten}";
"lng_selected_count#one" = "{count} Nachricht";
"lng_selected_count#other" = "{count} Nachrichten";
"lng_selected_cancel_sure_this" = "Upload abbrechen?";
"lng_selected_upload_stop" = "Stoppen";
"lng_selected_delete_sure_this" = "Diese Nachricht wirklich löschen?";
"lng_selected_delete_sure" = "Willst du {count:_not_used_|# Nachricht|# Nachrichten} löschen?";
"lng_selected_delete_sure#one" = "Möchtest du {count} Nachricht löschen?";
"lng_selected_delete_sure#other" = "Möchtest du {count} Nachrichten löschen?";
"lng_delete_photo_sure" = "Dieses Bild wirklich löschen?";
"lng_delete_for_everyone_hint" = "Das wird {count:_not_used_|sie|sie} bei allen im Chat löschen.";
"lng_delete_for_me_chat_hint" = "Das wird {count:_not_used_|sie|sie} nur bei dir löschen und nicht bei anderen Teilnehmern.";
"lng_delete_for_me_hint" = "Das wird {count:_not_used_|sie|sie} nur bei dir löschen.";
"lng_delete_for_everyone_hint#one" = "Das wird es bei allen im Chat löschen.";
"lng_delete_for_everyone_hint#other" = "Das wird sie bei allen im Chat löschen.";
"lng_delete_for_me_chat_hint#one" = "Das wird es nur bei dir löschen und nicht bei anderen Teilnehmern.";
"lng_delete_for_me_chat_hint#other" = "Das wird sie nur bei dir löschen und nicht bei anderen Teilnehmern.";
"lng_delete_for_me_hint#one" = "Das wird es nur bei dir löschen.";
"lng_delete_for_me_hint#other" = "Das wird sie nur bei dir löschen.";
"lng_delete_for_everyone_check" = "Bei allen löschen";
"lng_delete_for_other_check" = "Bei {user} löschen";
"lng_box_delete" = "Löschen";
@@ -1060,7 +1183,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_about_text_3" = "Fragen? Besuche doch {faq_open}unsere FAQ{faq_close} für mehr Infos.";
"lng_about_done" = "Fertig";
"lng_search_found_results" = "{count:Keine Nachrichten|# Nachricht|# Nachrichten}";
"lng_search_no_results" = "Keine Nachrichten gefunden";
"lng_search_found_results#one" = "{count} Nachricht gefunden";
"lng_search_found_results#other" = "{count} Nachrichten gefunden";
"lng_search_global_results" = "Globale Suchergebnisse";
"lng_media_save_progress" = "{ready} von {total} {mb}";
@@ -1169,11 +1294,133 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_player_message_yesterday" = "Gestern um {time}";
"lng_player_message_date" = "{date} um {time}";
"lng_rights_edit_admin" = "Administrator bearbeiten";
"lng_rights_edit_admin_header" = "Was kann dieser Admin machen?";
"lng_rights_about_add_admins_yes" = "Dieser Admin wird neue Admins mit gleichen (oder eingeschränkten) Rechten hinzufügen können.";
"lng_rights_about_add_admins_no" = "Dieser Admin kann keine neuen Admins hinzufügen.";
"lng_rights_about_admin_cant_edit" = "Du kannst die Berechtigungen für diesen Admin nicht ändern.";
"lng_rights_user_restrictions" = "Nutzerbeschränkungen";
"lng_rights_user_restrictions_header" = "Was kann dieser Nutzer machen?";
"lng_rights_channel_info" = "Kanal-Info ändern";
"lng_rights_channel_post" = "Nachrichten senden";
"lng_rights_channel_edit" = "Nachrichten von anderen bearbeiten";
"lng_rights_channel_delete" = "Nachrichten von anderen löschen";
"lng_rights_group_info" = "Gruppen-Info ändern";
"lng_rights_group_ban" = "Nutzer sperren";
"lng_rights_group_invite_link" = "Nutzer per Link einladen";
"lng_rights_group_invite" = "Nutzer hinzufügen";
"lng_rights_group_pin" = "Nachrichten anheften";
"lng_rights_group_delete" = "Nachrichten löschen";
"lng_rights_add_admins" = "Neue Admins hinzufügen";
"lng_rights_chat_read" = "Nachrichten lesen";
"lng_rights_chat_send_text" = "Nachrichten senden";
"lng_rights_chat_send_media" = "Medien senden";
"lng_rights_chat_send_stickers" = "Sticker & GIFs senden";
"lng_rights_chat_send_links" = "Linkvorschau senden";
"lng_rights_chat_banned_until_header" = "Eingeschränkt bis";
"lng_rights_chat_banned_forever" = "Dauerhaft";
"lng_rights_chat_banned_day#one" = "Für {count} Tag";
"lng_rights_chat_banned_day#other" = "Für {count} Tage";
"lng_rights_chat_banned_week#one" = "Für {count} Woche";
"lng_rights_chat_banned_week#other" = "Für {count} Wochen";
"lng_rights_chat_banned_custom" = "Eigener";
"lng_rights_chat_banned_custom_date" = "Bis {date}";
"lng_rights_chat_banned_block" = "Sperren und aus Gruppe entfernen";
"lng_restricted_send_message" = "Die Admins dieser Gruppe haben dir die Berechtigung entzogen, Nachrichten zu senden.";
"lng_restricted_send_media" = "Die Admins dieser Gruppe haben dir die Berechtigung entzogen, Medien zu senden.";
"lng_restricted_send_stickers" = "Die Admins dieser Gruppe haben dir die Berechtigung entzogen, Sticker zu senden.";
"lng_restricted_send_gifs" = "Die Admins dieser Gruppe haben dir die Berechtigung entzogen, GIFs zu senden.";
"lng_restricted_send_inline" = "Die Admins dieser Gruppe haben dir die Berechtigung entzogen, Inline-Bot Inhalte zu senden.";
"lng_restricted_list_title" = "Eingeschränke Nutzer";
"lng_banned_list_title" = "Gesperrte Nutzer";
"lng_admin_log_title_all" = "Alle Aktionen";
"lng_admin_log_title_selected" = "Ausgewählte Aktionen";
"lng_admin_log_filter" = "Filtern";
"lng_admin_log_filter_title" = "Filtern";
"lng_admin_log_filter_all_actions" = "Alle Aktionen";
"lng_admin_log_filter_restrictions" = "Neue Einschränkungen";
"lng_admin_log_filter_admins_new" = "Neue Admins";
"lng_admin_log_filter_members_new" = "Neue Mitglieder";
"lng_admin_log_filter_info_group" = "Gruppen-Info";
"lng_admin_log_filter_info_channel" = "Kanal-Info";
"lng_admin_log_filter_messages_deleted" = "Gelöschte Nachrichten";
"lng_admin_log_filter_messages_edited" = "Bearbeitete Nachrichten";
"lng_admin_log_filter_messages_pinned" = "Angeheftete Nachrichten";
"lng_admin_log_filter_members_removed" = "Ehemalige Mitglieder";
"lng_admin_log_filter_all_admins" = "Alle Nutzer";
"lng_admin_log_about" = "Was ist das?";
"lng_admin_log_about_text" = "Das ist eine Liste aller Aktionen, die von Gruppenmitgliedern und Admins in den letzten 48 Stunden durchgeführt wurden.";
"lng_admin_log_no_results_title" = "Keine Aktionen gefunden";
"lng_admin_log_no_results_text" = "Keine kürzlichen Ereignisse gefunden, die deinen Suchbegriff beinhalten.";
"lng_admin_log_no_results_search_text" = "Keine kürzlichen Aktionen gefunden, die '{query}' beinhalten.";
"lng_admin_log_no_events_title" = "Keine Aktionen gefunden";
"lng_admin_log_no_events_text" = "Es wurden keine Service-Aktionen \nvon Admins und Gruppenmitgliedern\nin den letzten 48 Stunden durchgeführt.";
"lng_admin_log_empty_text" = "Leer";
"lng_admin_log_changed_title_group" = "{from} hat den Gruppennamen in «{title}» geändert";
"lng_admin_log_changed_title_channel" = "{from} hat den Kanalnamen in «{title}» geändert";
"lng_admin_log_changed_description_group" = "{from} hat Gruppenbeschreibung bearbeitet:";
"lng_admin_log_removed_description_group" = "{from} hat Gruppenbeschreibung entfernt";
"lng_admin_log_changed_description_channel" = "{from} hat Kanalbeschreibung bearbeitet:";
"lng_admin_log_removed_description_channel" = "{from} hat Kanalbeschreibung entfernt";
"lng_admin_log_previous_description" = "Vorherige Beschreibung";
"lng_admin_log_changed_link_group" = "{from} hat Gruppen-Link geändert:";
"lng_admin_log_removed_link_group" = "{from} hat Gruppen-Link entfernt";
"lng_admin_log_changed_link_channel" = "{from} hat Kanal-Link geändert:";
"lng_admin_log_removed_link_channel" = "{from} hat Kanal-Link entfernt";
"lng_admin_log_previous_link" = "Vorheriger Link";
"lng_admin_log_changed_photo_group" = "{from} hat Gruppenbild geändert";
"lng_admin_log_changed_photo_channel" = "{from} hat Kanalbild geändert";
"lng_admin_log_removed_photo_group" = "{from} hat Gruppenbild entfernt";
"lng_admin_log_removed_photo_channel" = "{from} hat Kanalbild entfernt";
"lng_admin_log_invites_enabled" = "{from} hat Gruppeneinladungen aktiviert";
"lng_admin_log_invites_disabled" = "{from} hat Gruppeneinladungen deaktiviert";
"lng_admin_log_signatures_enabled" = "{from} hat Unterschriften aktiviert";
"lng_admin_log_signatures_disabled" = "{from} hat Unterschriften deaktiviert";
"lng_admin_log_pinned_message" = "{from} hat Nachricht angeheftet:";
"lng_admin_log_unpinned_message" = "{from} hat angeheftete Nachricht entfernt";
"lng_admin_log_edited_caption" = "{from} hat Beschriftung bearbeitet:";
"lng_admin_log_removed_caption" = "{from} hat Beschriftung entfernt";
"lng_admin_log_previous_caption" = "Original-Beschriftung";
"lng_admin_log_edited_message" = "{from} hat Nachricht bearbeitet:";
"lng_admin_log_previous_message" = "Original-Nachricht";
"lng_admin_log_deleted_message" = "{from} hat Nachricht gelöscht:";
"lng_admin_log_participant_joined" = "{from} ist der Gruppe beigetreten";
"lng_admin_log_participant_joined_channel" = "{from} ist dem Kanal beigetreten";
"lng_admin_log_participant_left" = "{from} hat die Gruppe verlassen";
"lng_admin_log_participant_left_channel" = "{from} hat den Kanal verlassen";
"lng_admin_log_invited" = "hat {user} eingeladen";
"lng_admin_log_banned" = "hat {user} gesperrt";
"lng_admin_log_restricted" = "hat Einschränkungen für {user} {until} geändert";
"lng_admin_log_promoted" = "hat Berechtigungen für {user} geändert";
"lng_admin_log_user_with_username" = "{name} ({mention})";
"lng_admin_log_restricted_forever" = "unbegrenzt";
"lng_admin_log_restricted_until" = "bis {date}";
"lng_admin_log_banned_view_messages" = "Nachrichten lesen";
"lng_admin_log_banned_send_messages" = "Nachrichten senden";
"lng_admin_log_banned_send_media" = "Medien senden";
"lng_admin_log_banned_send_stickers" = "Sticker & GIFs senden";
"lng_admin_log_banned_embed_links" = "Linkvorschau senden";
"lng_admin_log_admin_change_info" = "Info ändern";
"lng_admin_log_admin_post_messages" = "Nachrichten senden";
"lng_admin_log_admin_edit_messages" = "Nachrichten bearbeiten";
"lng_admin_log_admin_delete_messages" = "Nachrichten löschen";
"lng_admin_log_admin_ban_users" = "Nutzer sperren";
"lng_admin_log_admin_invite_users" = "Nutzer hinzufügen";
"lng_admin_log_admin_invite_link" = "Nutzer per Link einladen";
"lng_admin_log_admin_pin_messages" = "Nachrichten anheften";
"lng_admin_log_admin_add_admins" = "Neue Admins hinzufügen";
// Not used
"lng_topbar_info" = "Info";
"lng_profile_group_info" = "Gruppeninfo";
"lng_profile_channel_info" = "Info";
"lng_channel_add_admins" = "Neuer Administrator";
// Wnd specific

View File

@@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_menu_update" = "Actualizar";
"lng_menu_restart" = "Reiniciar";
"lng_menu_back" = "Atrás";
"lng_menu_night_mode" = "Modo noche";
"lng_disable_notifications_from_tray" = "Desactivar notificaciones";
"lng_enable_notifications_from_tray" = "Activar notificaciones";
@@ -88,7 +89,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_continue" = "Continuar";
"lng_close" = "Cerrar";
"lng_connecting" = "Conectando...";
"lng_reconnecting" = "Reconectar {count:ahora|en # s|en # s}...";
"lng_connecting_to_proxy" = "Conectando al proxy...";
"lng_connecting_settings" = "Ajustes";
"lng_reconnecting#one" = "Reconectar en {count} s...";
"lng_reconnecting#other" = "Reconectar en {count} s...";
"lng_reconnecting_try_now" = "Intentar ahora";
"lng_status_service_notifications" = "servicio de notificaciones";
@@ -102,8 +106,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_last_month" = "última vez hace unas semanas";
"lng_status_invisible" = "invisible";
"lng_status_lastseen_now" = "última vez hace un momento";
"lng_status_lastseen_minutes" = "última vez hace {count:_not_used_|# minuto|# minutos}";
"lng_status_lastseen_hours" = "última vez hace {count:_not_used_|# hora|# horas}";
"lng_status_lastseen_minutes#one" = "última vez hace {count} minuto";
"lng_status_lastseen_minutes#other" = "última vez hace {count} minutos";
"lng_status_lastseen_hours#one" = "última vez hace {count} hora";
"lng_status_lastseen_hours#other" = "última vez hace {count} horas";
"lng_status_lastseen_today" = "última vez hoy a las {time}";
"lng_status_lastseen_yesterday" = "última vez ayer a las {time}";
"lng_status_lastseen_date" = "última vez el {date}";
@@ -112,14 +118,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_connecting" = "conectando...";
"lng_chat_status_unaccessible" = "grupo inaccesible";
"lng_chat_status_members" = "{count:sin miembros|# miembro|# miembros}";
"lng_chat_status_members_online" = "{count:_not_used_|# miembro|# miembros}, {count_online:_not_used_|# en línea|# en línea}";
"lng_chat_status_no_members" = "sin miembros";
"lng_chat_status_members#one" = "{count} miembro";
"lng_chat_status_members#other" = "{count} miembros";
"lng_chat_status_online#one" = "{count} en línea";
"lng_chat_status_online#other" = "{count} en línea";
"lng_chat_status_members_online" = "{members_count}, {online_count}";
"lng_channel_status" = "canal";
"lng_group_status" = "grupo";
"lng_channel_members_link" = "{count:_not_used_|# miembro|# miembros}";
"lng_channel_admins_link" = "{count:_not_used_|# administrador|# administradores}";
"lng_channel_members_link#one" = "{count} miembro";
"lng_channel_members_link#other" = "{count} miembros";
"lng_channel_admins_link#one" = "{count} administrador";
"lng_channel_admins_link#other" = "{count} administradores";
"lng_server_error" = "Error interno del servidor.";
"lng_flood_error" = "Muchos intentos. Por favor, reinténtalo más tarde.";
@@ -128,8 +140,21 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_join_channel_error" = "Lo sentimos, te has unido a demasiados canales y supergrupos. Por favor, sal de algunos antes de unirte.";
"lng_error_phone_flood" = "Has eliminado y vuelto a crear tu cuenta muchas veces recientemente. Por favor, espera algunos días antes de inscribirte de nuevo.";
"lng_error_start_minimized_passcoded" = "Has configurado un código de acceso, así que no puedes iniciar la app minimizada. La app te pedirá el código de acceso para que empiece a funcionar.";
"lng_error_pinned_max" = "Lo sentimos, no puedes anclar más de {count:_not_used_|# chat|# chats}.";
"lng_error_pinned_max#one" = "Lo sentimos, no puedes anclar más de {count} chat.";
"lng_error_pinned_max#other" = "Lo sentimos, no puedes anclar más de {count} chats.";
"lng_error_public_groups_denied" = "Lamentablemente, tienes prohibido participar en grupos públicos.\n{more_info}";
"lng_error_cant_edit_admin" = "No puedes editar los permisos de este administrador.";
"lng_error_cant_add_member" = "No puedes añadir al bot a este grupo. Pide a algún administrador que lo haga.";
"lng_error_cant_add_bot" = "Este bot no puede se añadido a grupos.";
"lng_error_cant_add_admin_invite" = "No puedes añadir a este usuario como administrador porque no es un miembro de este grupo y no puedes invitarlo.";
"lng_error_cant_add_admin_unban" = "No puedes añadir a este usuario como administrador porque está en la lista de suspendidos y no puedes quitar su suspensión.";
"lng_error_cant_ban_admin" = "No puedes suspender a este usuario porque es un administrador del grupo y no puedes degradarlo.";
"lng_sure_add_admin_invite" = "Este usuario no es miembro del grupo. ¿Quieres añadirlo y ascenderlo a administrador?";
"lng_sure_add_admin_unban" = "Este usuario está restringido o suspendido del grupo. ¿Quieres quitar su suspensión y ascenderlo a administrador?";
"lng_sure_ban_admin" = "Este usuario es administrador del grupo. ¿Quieres continuar y restringirlo?";
"lng_sure_ban_user_group" = "¿Suspender a {user} en el grupo?";
"lng_sure_enable_socks" = "¿Quieres activar este proxy?\n\nServidor: {server}\nPuerto: {port}\n\nPuedes cambiar tu servidor proxy más tarde en Ajustes (Tipo de conexión).";
"lng_sure_enable" = "Activar";
"lng_edit_deleted" = "Este mensaje fue eliminado";
"lng_edit_too_long" = "Tu texto es demasiado largo";
@@ -157,7 +182,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_phone_ph" = "Tu número de teléfono";
"lng_phone_title" = "Tu teléfono";
"lng_phone_desc" = "Por favor, confirma el código de tu país\ny tu número de teléfono.";
"lng_phone_notreg" = "Si no tienes una cuenta de Telegram todavía, \npor favor, [b]regístrate[/b] con {link_start}Android / iPhone{link_end}\no aquí {signup_start}here{signup_end}";
"lng_phone_notreg" = "Si no tienes una cuenta de Telegram todavía, \npor favor, [b]regístrate[/b] con {link_start}Android / iPhone{link_end}\no {signup_start}aquí{signup_end}";
"lng_country_code" = "Código de país";
"lng_bad_country_code" = "Código de país inválido";
"lng_country_ph" = "Buscar";
@@ -198,9 +223,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_signin_sure_reset" = "¡Advertencia!\n\n¡Perderás todos tus chats y mensajes, junto con la multimedia y archivos compartidos!\n\n¿Quieres restablecer tu cuenta?";
"lng_signin_reset" = "Restablecer";
"lng_signin_reset_wait" = "Como la cuenta {phone_number} está activa y protegida con una contraseña, la eliminaremos en 1 semana, por motivos de seguridad. Puedes cancelar el proceso en cualquier momento.\n\nPodrás restablecer tu cuenta en:\n{when}";
"lng_signin_reset_in_days" = "{count_days:0 días|# día|# días} {count_hours:0 horas|# hora|# horas} {count_minutes:0 minutos|# minuto|# minutos}";
"lng_signin_reset_in_hours" = "{count_hours:0 horas|# hora|# horas} {count_minutes:0 minutos|# minuto|# minutos}";
"lng_signin_reset_in_minutes" = "{count_minutes:0 minutos|# minuto|# minutos}";
"lng_signin_reset_days#one" = "{count} día";
"lng_signin_reset_days#other" = "{count} días";
"lng_signin_reset_hours#one" = "{count} hora";
"lng_signin_reset_hours#other" = "{count} horas";
"lng_signin_reset_minutes#one" = "{count} minuto";
"lng_signin_reset_minutes#other" = "{count} minutos";
"lng_signin_reset_in_days" = "{days_count} {hours_count} {minutes_count}";
"lng_signin_reset_in_hours" = "{hours_count} {minutes_count}";
"lng_signin_reset_cancelled" = "Tus intentos recientes para restablecer la cuenta fueron cancelados por su usuario activo. Por favor, reinténtalo en 7 días.";
"lng_signup_title" = "Tu información";
@@ -223,7 +253,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_settings_save" = "Guardar";
"lng_settings_upload" = "Poner foto de perfil";
"lng_settings_crop_profile" = "Selecciona el área para tu foto de perfil";
"lng_settings_crop_profile" = "Selecciona un área para tu foto de perfil";
"lng_settings_uploading_photo" = "Subiendo foto...";
"lng_settings_edit" = "Editar";
"lng_settings_drop_area_subtitle" = "para ponerla como tu foto";
@@ -241,10 +271,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_username_link" = "Este enlace abre un chat contigo:";
"lng_username_copied" = "Enlace copiado al portapapeles.";
"lng_bio_title" = "Editar tu biografía";
"lng_bio_placeholder" = "Biografía";
"lng_bio_about" = "Puedes añadir algunas palabras sobre ti. Cualquiera que abra tu perfil verá este texto.";
"lng_settings_section_info" = "Información";
"lng_settings_phone_number" = "Número de teléfono:";
"lng_settings_username" = "Alias:";
"lng_settings_choose_username" = "Elige un alias";
"lng_settings_empty_bio" = "Ninguna";
"lng_settings_section_notify" = "Notificaciones";
"lng_settings_desktop_notify" = "Notificaciones de escritorio";
@@ -303,7 +338,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_backgrounds_header" = "Elige tu nuevo fondo de chat";
"lng_theme_sure_keep" = "¿Mantener este tema?";
"lng_theme_reverting" = "Regresar al tema anterior en {count:_not_used_|# segundo|# segundos}.";
"lng_theme_reverting#one" = "Regresar al tema anterior en {count} segundo.";
"lng_theme_reverting#other" = "Regresar al tema anterior en {count} segundos.";
"lng_theme_keep_changes" = "Mantener cambios";
"lng_theme_revert" = "Revertir";
@@ -329,14 +365,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_local_storage_title" = "Almacenamiento local";
"lng_settings_no_data_cached" = "¡No se encontraron datos en la caché!";
"lng_settings_images_cached" = "{count:_not_used_|# imagen|# imágenes}, {size}";
"lng_settings_audios_cached" = "{count:_not_used_|# mensaje de voz|# mensajes de voz}, {size}";
"lng_settings_images_cached#one" = "{count} imagen, {size}";
"lng_settings_images_cached#other" = "{count} imágenes, {size}";
"lng_settings_audios_cached#one" = "{count} mensaje de voz, {size}";
"lng_settings_audios_cached#other" = "{count} mensajes de voz, {size}";
"lng_local_storage_clear" = "Eliminar todo";
"lng_local_storage_clearing" = "Borrando...";
"lng_local_storage_cleared" = "¡Borrado!";
"lng_local_storage_clear_failed" = "Borrado fallido :(";
"lng_settings_section_advanced_settings" = "Ajustes avanzados";
"lng_settings_enable_night_theme" = "Activar modo noche";
"lng_settings_disable_night_theme" = "Desactivar modo noche";
"lng_passcode_remove_button" = "Quitar";
@@ -348,8 +388,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_passcode_autolock" = "Autobloqueo";
"lng_passcode_autolock_away" = "Autobloqueo si estoy fuera:";
"lng_passcode_autolock_inactive" = "Autobloqueo si estoy inactivo:";
"lng_passcode_autolock_minutes" = "{count:_not_used_|# minuto|# minutos}";
"lng_passcode_autolock_hours" = "{count:_not_used_|# hora|# horas}";
"lng_passcode_autolock_minutes#one" = "{count} minuto";
"lng_passcode_autolock_minutes#other" = "{count} minutos";
"lng_passcode_autolock_hours#one" = "{count} hora";
"lng_passcode_autolock_hours#other" = "{count} horas";
"lng_passcode_enter_old" = "Pon el código de acceso actual";
"lng_passcode_enter_first" = "Pon un código de acceso";
"lng_passcode_enter_new" = "Pon tu nuevo código";
@@ -455,32 +497,46 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_privacy_lastseen_title" = "Última conexión";
"lng_edit_privacy_lastseen_description" = "Puedes elegir quién ve tu última conexión:";
"lng_edit_privacy_lastseen_warning" = "Importante: No verás la última conexión de las personas con las que no compartes la tuya. En su lugar, se mostrarán conexiones indeterminadas (recientemente, hace unos días, hace unas semanas).";
"lng_edit_privacy_lastseen_always" = "Compartir con{count:| # usuario| # usuarios}";
"lng_edit_privacy_lastseen_never" = "No compartir con{count:| # usuario| # usuarios}";
"lng_edit_privacy_lastseen_always_empty" = "Compartir con";
"lng_edit_privacy_lastseen_always#one" = "Compartir con {count} usuario";
"lng_edit_privacy_lastseen_always#other" = "Compartir con {count} usuarios";
"lng_edit_privacy_lastseen_never_empty" = "No compartir con";
"lng_edit_privacy_lastseen_never#one" = "No compartir con {count} usuario";
"lng_edit_privacy_lastseen_never#other" = "No compartir con {count} usuarios";
"lng_edit_privacy_lastseen_exceptions" = "Estos ajustes anularán los valores de arriba.";
"lng_edit_privacy_lastseen_always_title" = "Compartir con";
"lng_edit_privacy_lastseen_never_title" = "No compartir con";
"lng_edit_privacy_groups_title" = "Invitación a grupos";
"lng_edit_privacy_groups_description" = "Puedes restringir quién puede añadirte a grupos o canales con gran precisión:";
"lng_edit_privacy_groups_always" = "Permitir{count:| # usuario| # usuarios}";
"lng_edit_privacy_groups_never" = "No permitir{count:| # usuario| # usuarios}";
"lng_edit_privacy_groups_always_empty" = "Permitir";
"lng_edit_privacy_groups_always#one" = "Permitir {count} usuario";
"lng_edit_privacy_groups_always#other" = "Permitir {count} usuarios";
"lng_edit_privacy_groups_never_empty" = "No permitir";
"lng_edit_privacy_groups_never#one" = "No permitir {count} usuario";
"lng_edit_privacy_groups_never#other" = "No permitir {count} usuarios";
"lng_edit_privacy_groups_exceptions" = "Estos usuarios podrán o no añadirte a grupos y canales, independiente de los ajustes de arriba.";
"lng_edit_privacy_groups_always_title" = "Permitir";
"lng_edit_privacy_groups_never_title" = "No permitir";
"lng_edit_privacy_calls_title" = "Privacidad de llamadas";
"lng_edit_privacy_calls_description" = "Puedes restringir quién puede llamarte:";
"lng_edit_privacy_calls_always" = "Permitir{count:| # usuario| # usuarios}";
"lng_edit_privacy_calls_never" = "No permitir{count:| # usuario| # usuarios}";
"lng_edit_privacy_calls_always_empty" = "Permitir";
"lng_edit_privacy_calls_always#one" = "Permitir {count} usuario";
"lng_edit_privacy_calls_always#other" = "Permitir {count} usuarios";
"lng_edit_privacy_calls_never_empty" = "No permitir";
"lng_edit_privacy_calls_never#one" = "No permitir {count} usuario";
"lng_edit_privacy_calls_never#other" = "No permitir {count} usuarios";
"lng_edit_privacy_calls_exceptions" = "Estos usuarios podrán o no llamarte, independiente de los ajustes de arriba.";
"lng_edit_privacy_calls_always_title" = "Permitir";
"lng_edit_privacy_calls_never_title" = "No permitir";
"lng_self_destruct_title" = "Autodestrucción de la cuenta";
"lng_self_destruct_description" = "Si no estás en línea durante este tiempo, al menos una vez, tu cuenta se eliminará con todos tus grupos, mensajes y contactos.";
"lng_self_destruct_months" = "{count:_not_used_|# mes|# meses}";
"lng_self_destruct_years" = "{count:_not_used_|# año|# años}";
"lng_self_destruct_months#one" = "{count} mes";
"lng_self_destruct_months#other" = "{count} meses";
"lng_self_destruct_years#one" = "{count} año";
"lng_self_destruct_years#other" = "{count} años";
"lng_change_phone_title" = "Cambiar número de teléfono";
"lng_change_phone_description" = "Puedes cambiar tu número de Telegram aquí. \nTu cuenta y todos tus datos de la nube, mensajes,\narchivos, grupos, contactos, etc., se moverán al \nnuevo número.\n\n[b]Importante[/b]: Todos tus contactos de Telegram\ntendrán tu [b]nuevo número[/b] añadido a sus \nagendas de contactos, siempre que hayan tenido \ntu número viejo y no los hayas bloqueado.";
@@ -506,15 +562,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_invite_link_section" = "Enlace de invitación";
"lng_profile_create_public_link" = "Crear enlace público";
"lng_profile_edit_public_link" = "Editar enlace público";
"lng_profile_search_members" = "Buscar miembros";
"lng_profile_manage_admins" = "Gestionar administradores";
"lng_profile_manage_blocklist" = "Administrar usuarios bloqueados";
"lng_profile_common_groups" = "{count:_not_used_|# grupo|# grupos} en común";
"lng_profile_manage_blocklist" = "Administrar usuarios suspendidos";
"lng_profile_manage_restrictedlist" = "Administrar usuarios restringidos";
"lng_profile_recent_actions" = "Acciones recientes";
"lng_profile_common_groups#one" = "{count} grupo en común";
"lng_profile_common_groups#other" = "{count} grupos en común";
"lng_profile_common_groups_section" = "Grupos en común";
"lng_profile_participants_section" = "Miembros";
"lng_profile_info_section" = "Información";
"lng_profile_mobile_number" = "Número:";
"lng_profile_username" = "Alias:";
"lng_profile_link" = "Enlace:";
"lng_profile_bio" = "Biografía:";
"lng_profile_add_contact" = "Añadir contacto";
"lng_profile_edit_contact" = "Editar";
"lng_profile_enable_notifications" = "Notificaciones";
@@ -542,23 +603,30 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_delete_and_exit" = "Salir";
"lng_profile_kick" = "Eliminar";
"lng_profile_admin" = "administrador";
"lng_profile_edit_permissions" = "Editar";
"lng_profile_sure_kick" = "¿Eliminar a {user} del grupo?";
"lng_profile_sure_kick_channel" = "¿Eliminar a {user} del canal?";
"lng_profile_sure_kick_admin" = "¿Eliminar a {user} de los administradores?";
"lng_profile_loading" = "Cargando...";
"lng_profile_shared_media" = "Multimedia";
"lng_profile_no_media" = "No hay multimedia en este chat.";
"lng_profile_photos" = "{count:_not_used_|# foto|# fotos}";
"lng_profile_photos#one" = "{count} foto";
"lng_profile_photos#other" = "{count} fotos";
"lng_profile_photos_header" = "Fotos";
"lng_profile_videos" = "{count:_not_used_|# vídeo|# vídeos}";
"lng_profile_videos#one" = "{count} vídeo";
"lng_profile_videos#other" = "{count} vídeos";
"lng_profile_videos_header" = "Vídeos";
"lng_profile_songs" = "{count:_not_used_|# audio|# audios}";
"lng_profile_songs#one" = "{count} audio";
"lng_profile_songs#other" = "{count} audios";
"lng_profile_songs_header" = "Audios";
"lng_profile_files" = "{count:_not_used_|# archivo|# archivos}";
"lng_profile_files#one" = "{count} archivo";
"lng_profile_files#other" = "{count} archivos";
"lng_profile_files_header" = "Archivos";
"lng_profile_audios" = "{count:_not_used_|# mensaje de voz|# mensajes de voz}";
"lng_profile_audios#one" = "{count} mensaje de voz";
"lng_profile_audios#other" = "{count} mensajes de voz";
"lng_profile_audios_header" = "Mensajes de voz";
"lng_profile_shared_links" = "{count:_not_used_|# enlace|# enlaces}";
"lng_profile_shared_links#one" = "{count} enlace";
"lng_profile_shared_links#other" = "{count} enlaces";
"lng_profile_shared_links_header" = "Enlaces";
"lng_profile_copy_phone" = "Copiar número de teléfono";
"lng_profile_copy_fullname" = "Copiar nombre";
@@ -578,16 +646,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_report_button" = "Reportar";
"lng_report_thanks" = "¡Gracias! Tu reporte será revisado por nuestro equipo muy pronto.";
"lng_channel_add_admins" = "Nuevo administrador";
"lng_channel_add_members" = "Añadir miembros";
"lng_channel_add_banned" = "Suspender usuario";
"lng_channel_add_restricted" = "Restringir usuario";
"lng_channel_members" = "Miembros";
"lng_channel_only_last_shown" = "Sólo {count:_not_used_|aparece el último miembro|aparecen los últimos # miembros}";
"lng_channel_only_last_shown#one" = "Sólo aparece el último miembro";
"lng_channel_only_last_shown#other" = "Sólo aparecen los últimos {count} miembros";
"lng_channel_admins" = "Administradores";
"lng_channel_add_admin" = "Añadir administrador";
"lng_channel_admin_sure" = "¿Añadir a {user} como administrador?";
"lng_channel_admins_too_much" = "Lo sentimos, alcanzaste el límite de administradores. Por favor, elimina un administrador primero.";
"lng_channel_admin_status_creator" = "Creador";
"lng_channel_admin_status_promoted_by" = "Ascendido por {user}";
"lng_channel_admin_status_not_admin" = "No es administrador";
"lng_group_blocked_list_about" = "Los usuarios bloqueados son eliminados del grupo y sólo pueden volver si son invitados por un administrador.\n\nLos enlaces de invitación no funcionan para ellos.";
"lng_group_blocked_list_about" = "Los usuarios suspendidos son eliminados del grupo y sólo pueden volver si son invitados por un administrador. \nLos enlaces de invitación no funcionan para ellos.";
"lng_chat_all_members_admins" = "Todos son administradores";
"lng_chat_about_all_admins" = "Todos pueden añadir nuevos miembros, editar el nombre y la foto del grupo.";
@@ -595,7 +667,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_participant_filter" = "Buscar";
"lng_participant_invite" = "Invitar";
"lng_participant_invite_sorry" = "Lo sentimos, sólo puedes añadir {count:_not_used|# miembros|# miembros} a un canal personalmente.\n\nAhora, las personas deben unirse con un enlace de invitación.";
"lng_participant_invite_sorry#one" = "Lo sentimos, sólo puedes añadir a {count} miembro a un canal. \n\nAhora será necesario un enlace de invitación para unirse.";
"lng_participant_invite_sorry#other" = "Lo sentimos, sólo puedes añadir a los primeros {count} miembros a un canal. \n\nAhora será necesario un enlace de invitación para unirse.";
"lng_create_group_back" = "Atrás";
"lng_create_group_next" = "Siguiente";
"lng_create_group_create" = "Crear";
@@ -618,8 +691,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_create_channel_link_available" = "Este enlace está disponible";
"lng_create_channel_link_copied" = "Enlace copiado al portapapeles";
"lng_create_group_crop" = "Selecciona el área para la foto del grupo";
"lng_create_channel_crop" = "Selecciona el área para la foto del canal";
"lng_create_group_crop" = "Selecciona un área para la foto del grupo";
"lng_create_channel_crop" = "Selecciona un área para la foto del canal";
"lng_failed_add_participant" = "No se pudo añadir al usuario. Por favor, reinténtalo más tarde.";
"lng_failed_add_not_mutual" = "Lo sentimos, si una persona sale del grupo, sólo un contacto mutuo puede volver a invitarlo (necesitan tener tu número y tú el suyo).";
@@ -637,8 +710,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_message_empty" = "Mensaje vacío";
"lng_message_unsupported" = "Este mensaje no es soportado por tu versión de Telegram Desktop. Por favor, actualiza a la última versión desde Ajustes o instálala desde {link}";
"lng_duration_seconds" = "{count:_not_used_|# segundo|# segundos}";
"lng_duration_minutes_seconds" = "{count_minutes:# min|# min|# min} {count_seconds:# s|# s|# s}";
"lng_duration_seconds#one" = "{count} segundo";
"lng_duration_seconds#other" = "{count} segundos";
"lng_duration_minsec_minutes#one" = "{count} min";
"lng_duration_minsec_minutes#other" = "{count} min";
"lng_duration_minsec_seconds#one" = "{count} s";
"lng_duration_minsec_seconds#other" = "{count} s";
"lng_duration_minutes_seconds" = "{minutes_count} {seconds_count}";
"lng_action_add_user" = "{from} añadió a {user}";
"lng_action_add_users_many" = "{from} añadió a {users}";
@@ -668,7 +746,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_action_pinned_media_video" = "un vídeo";
"lng_action_pinned_media_audio" = "un audio";
"lng_action_pinned_media_voice" = "un mensaje de voz";
"lng_action_pinned_media_video_message" = "un mensaje de vídeo";
"lng_action_pinned_media_video_message" = "un videomensaje";
"lng_action_pinned_media_file" = "un archivo";
"lng_action_pinned_media_gif" = "un GIF";
"lng_action_pinned_media_contact" = "un contacto";
@@ -676,18 +754,33 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_action_pinned_media_sticker" = "un sticker";
"lng_action_pinned_media_emoji_sticker" = "un {emoji} sticker";
"lng_action_pinned_media_game" = "el juego «{game}»";
"lng_action_game_score" = "{from} consiguió {count:# puntos|# punto|# puntos} en {game}";
"lng_action_game_you_scored" = "Conseguiste {count:# puntos|# punto|# puntos} en {game}";
"lng_action_game_score_no_game" = "{from} consiguió {count:# puntos|# punto|# puntos}";
"lng_action_game_you_scored_no_game" = "Conseguiste {count:# puntos|# punto|# puntos}";
"lng_action_game_score#one" = "{from} consiguió {count} punto en {game}";
"lng_action_game_score#other" = "{from} consiguió {count} puntos en {game}";
"lng_action_game_you_scored#one" = "Conseguiste {count} punto en {game}";
"lng_action_game_you_scored#other" = "Conseguiste {count} puntos en {game}";
"lng_action_game_score_no_game#one" = "{from} consiguió {count} punto";
"lng_action_game_score_no_game#other" = "{from} consiguió {count} puntos";
"lng_action_game_you_scored_no_game#one" = "Conseguiste {count} punto";
"lng_action_game_you_scored_no_game#other" = "Conseguiste {count} puntos";
"lng_action_payment_done" = "Has transferido satisfactoriamente {amount} a {user}";
"lng_action_payment_done_for" = "Has transferido satisfactoriamente {amount} a {name} por {invoice}";
"lng_action_took_screenshot" = "¡{from} hizo una captura de pantalla!";
"lng_action_you_took_screenshot" = "¡Hiciste una captura de pantalla!";
"lng_profile_migrate_reached" = "Límite de {count:_not_used_|# miembro|# miembros} alcanzado";
"lng_ttl_photo_received" = "{from} te envió una foto con autodestrucción. Por favor, mírala en tu teléfono.";
"lng_ttl_photo_sent" = "Enviaste una foto con autodestrucción";
"lng_ttl_photo_expired" = "La foto expiró";
"lng_ttl_video_received" = "{from} te envió un vídeo con autodestrucción. Por favor, míralo en tu teléfono.";
"lng_ttl_video_sent" = "Enviaste un vídeo con autodestrucción";
"lng_ttl_video_expired" = "El vídeo expiró";
"lng_profile_migrate_reached#one" = "Límite de {count} miembro alcanzado";
"lng_profile_migrate_reached#other" = "Límite de {count} miembros alcanzado";
"lng_profile_migrate_body" = "Para superar este límite, puedes convertir tu grupo en un supergrupo.";
"lng_profile_migrate_learn_more" = "Saber más »";
"lng_profile_migrate_about" = "Para superar el límite y tener características adicionales, conviértelo en un supergrupo:";
"lng_profile_migrate_feature1" = "— Permiten hasta {count:_not_used_|# usuario|# usuarios}";
"lng_profile_migrate_feature1#one" = "— Permiten hasta {count} usuario";
"lng_profile_migrate_feature1#other" = "— Permiten hasta {count} usuarios";
"lng_profile_migrate_feature2" = "— Nuevos miembros ven todo el historial";
"lng_profile_migrate_feature3" = "— Un admin. borra mensajes para todos";
"lng_profile_migrate_feature4" = "— Notificaciones silenciadas por defecto";
@@ -702,7 +795,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_convert_feature4" = "— El creador puede generar un enlace público";
"lng_profile_convert_warning" = "{bold_start}Importante:{bold_end} Esta acción no se puede deshacer";
"lng_profile_convert_confirm" = "Convertir";
"lng_profile_add_more_after_upgrade" = "Podrás añadir hasta {count:_not_used_|# miembro|# miembros} una vez que conviertas tu grupo en un supergrupo.";
"lng_profile_add_more_after_upgrade#one" = "Podrás añadir hasta {count} miembro una vez que conviertas tu grupo en un supergrupo.";
"lng_profile_add_more_after_upgrade#other" = "Podrás añadir hasta {count} miembros una vez que conviertas tu grupo en un supergrupo.";
"lng_channel_not_accessible" = "Lo sentimos, este canal no es accesible.";
"lng_group_not_accessible" = "Lo sentimos, este grupo no es accesible.";
@@ -717,7 +811,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_group_invite_want_join_channel" = "¿Quieres unirte al canal «{title}»?";
"lng_group_invite_join" = "Unirme";
"lng_group_invite_members" = "{count:_not_used_|# miembro|# miembros}, entre ellos:";
"lng_group_invite_members#one" = "{count} miembro, entre ellos:";
"lng_group_invite_members#other" = "{count} miembros, entre ellos:";
"lng_group_invite_link" = "Enlace de invitación:";
"lng_group_invite_create" = "Crear un enlace de invitación";
@@ -730,6 +825,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_channel_public_link_copied" = "Enlace copiado al portapapeles.";
"lng_forwarded" = "Reenviado desde {user}";
"lng_forwarded_date" = "Original: {date}";
"lng_forwarded_channel" = "Reenviado desde {channel}";
"lng_forwarded_via" = "Reenviado desde {user} vía {inline_bot}";
"lng_forwarded_channel_via" = "Reenviado desde {channel} vía {inline_bot}";
@@ -809,7 +905,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_stickers_you_have" = "Administrar y ordenar los packs de stickers";
"lng_stickers_featured" = "Stickers destacados";
"lng_stickers_return" = "Deshacer";
"lng_stickers_count" = "{count:Cargando...|# sticker|# stickers}";
"lng_stickers_count#one" = "{count} sticker";
"lng_stickers_count#other" = "{count} stickers";
"lng_stickers_masks_pack" = "Este es un pack de stickers de máscaras. Puedes usarlas en el editor de fotos de nuestras aplicaciones en móviles.";
"lng_in_dlg_photo" = "Foto";
@@ -817,7 +914,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_in_dlg_audio_file" = "Audio";
"lng_in_dlg_contact" = "Contacto";
"lng_in_dlg_audio" = "Mensaje de voz";
"lng_in_dlg_video_message" = "Mensaje de vídeo";
"lng_in_dlg_video_message" = "Videomensaje";
"lng_in_dlg_file" = "Archivo";
"lng_in_dlg_sticker" = "Sticker";
"lng_in_dlg_sticker_emoji" = "{emoji} Sticker";
@@ -839,6 +936,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_cant_invite_banned" = "Sólo el administrador puede añadir a este usuario.";
"lng_cant_invite_privacy" = "No puedes añadir a este usuario a grupos por sus ajustes de privacidad.";
"lng_cant_invite_privacy_channel" = "No puedes añadir a este usuario a canales por sus ajustes de privacidad.";
"lng_cant_delete_group#one" = "No es posible eliminar manualmente comunidades que tienen más de {count} miembro. Por favor, contacta al soporte de Telegram si quieres eliminar el grupo.";
"lng_cant_delete_group#other" = "No es posible eliminar manualmente comunidades que tienen más de {count} miembros. Por favor, contacta al soporte de Telegram si quieres eliminar el grupo.";
"lng_cant_delete_channel#one" = "No es posible eliminar manualmente comunidades que tienen más de {count} miembro. Por favor, contacta al soporte de Telegram si quieres eliminar el canal.";
"lng_cant_delete_channel#other" = "No es posible eliminar manualmente comunidades que tienen más de {count} miembros. Por favor, contacta al soporte de Telegram si quieres eliminar el canal.";
"lng_cant_do_this" = "Lo sentimos, esta acción no está disponible.";
"lng_send_button" = "Enviar";
@@ -884,11 +985,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_typing" = "escribiendo";
"lng_user_typing" = "{user} está escribiendo";
"lng_users_typing" = "{user} y {second_user} están escribiendo";
"lng_many_typing" = "{count:_not_used_|# está|# están} escribiendo";
"lng_many_typing#one" = "{count} está escribiendo";
"lng_many_typing#other" = "{count} están escribiendo";
"lng_playing_game" = "jugando";
"lng_user_playing_game" = "{user} está jugando";
"lng_users_playing_game" = "{user} y {second_user} están jugando";
"lng_many_playing_game" = "{count:_not_used_|# está|# están} jugando";
"lng_many_playing_game#one" = "{count} está jugando";
"lng_many_playing_game#other" = "{count} están jugando";
"lng_send_action_record_video" = "grabando un vídeo";
"lng_user_action_record_video" = "{user} está grabando un vídeo";
"lng_send_action_upload_video" = "enviando un vídeo";
@@ -897,10 +1000,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_user_action_record_audio" = "{user} está grabando un mensaje de voz";
"lng_send_action_upload_audio" = "enviando un mensaje de voz";
"lng_user_action_upload_audio" = "{user} está enviando un mensaje de voz";
"lng_send_action_record_round" = "grabando un mensaje de vídeo";
"lng_user_action_record_round" = "{user} está grabando un mensaje de vídeo";
"lng_send_action_upload_round" = "enviando un mensaje de vídeo";
"lng_user_action_upload_round" = "{user} está enviando un mensaje de vídeo";
"lng_send_action_record_round" = "grabando un videomensaje";
"lng_user_action_record_round" = "{user} está grabando un videomensaje";
"lng_send_action_upload_round" = "enviando un videomensaje";
"lng_user_action_upload_round" = "{user} está enviando un videomensaje";
"lng_send_action_upload_photo" = "enviando una foto";
"lng_user_action_upload_photo" = "{user} está enviando una foto";
"lng_send_action_upload_file" = "enviando un archivo";
@@ -909,7 +1012,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_user_action_geo_location" = "{user} está obteniendo una ubicación";
"lng_send_action_choose_contact" = "eligiendo un contacto";
"lng_user_action_choose_contact" = "{user} está eligiendo un contacto";
"lng_unread_bar" = "{count:_not_used_|# mensaje sin leer|# mensajes sin leer}";
"lng_unread_bar#one" = "{count} mensaje no leído";
"lng_unread_bar#other" = "{count} mensajes no leídos";
"lng_maps_point" = "Ubicación";
"lng_save_photo" = "Guardar imagen";
@@ -932,7 +1036,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_from_top" = "Desanclar";
"lng_context_promote_admin" = "Nombrar administrador";
"lng_context_remove_admin" = "Eliminar de los administradores";
"lng_context_edit_permissions" = "Editar permisos";
"lng_context_restrict_user" = "Restringir usuario";
"lng_context_remove_from_group" = "Eliminar del grupo";
"lng_context_copy_link" = "Copiar enlace";
@@ -970,31 +1075,41 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_msg" = "Desanclar mensaje";
"lng_context_cancel_upload" = "Cancelar envío";
"lng_context_copy_selected" = "Copiar el texto seleccionado";
"lng_context_copy_selected_items" = "Copiar como texto";
"lng_context_forward_selected" = "Reenviar lo seleccionado";
"lng_context_delete_selected" = "Eliminar lo seleccionado";
"lng_context_clear_selection" = "Eliminar selección";
"lng_really_send_image" = "¿Quieres enviar esta imagen?";
"lng_really_send_file" = "¿Quieres enviar este archivo?";
"lng_really_share_contact" = "¿Quieres compartir este contacto?";
"lng_send_images_compress" = "Comprimir {count:_not_used_|imagen|imágenes}";
"lng_send_images_compress#one" = "Comprimir imagen";
"lng_send_images_compress#other" = "Comprimir imágenes";
"lng_send_image_non_local" = "No se pudo enviar el archivo remoto: {name}";
"lng_send_image_empty" = "No se pudo enviar el archivo vacío: {name}";
"lng_send_image_too_large" = "No se pudo enviar el archivo porque supera los 1500 MB: {name}";
"lng_send_folder" = "No se pudo enviar «{name}», porque es un directorio :(";
"lng_send_images_selected" = "{count:_not_used_|# imagen seleccionada|# imágenes seleccionadas}";
"lng_send_photos" = "Enviar {count:_not_used_|# foto|# fotos}";
"lng_send_files_selected" = "{count:_not_used_|# archivo seleccionado|# archivos seleccionados}";
"lng_send_files" = "Enviar {count:_not_used_|# archivo|# archivos}";
"lng_send_images_selected#one" = "{count} imagen seleccionada";
"lng_send_images_selected#other" = "{count} imágenes seleccionadas";
"lng_send_photos#one" = "Enviar {count} foto";
"lng_send_photos#other" = "Enviar {count} fotos";
"lng_send_files_selected#one" = "{count} archivo seleccionado";
"lng_send_files_selected#other" = "{count} archivos seleccionados";
"lng_send_files#one" = "Enviar {count} archivo";
"lng_send_files#other" = "Enviar {count} archivos";
"lng_forward_choose" = "Elige un destinatario...";
"lng_forward_cant" = "Lo sentimos, no puedes reenviar aquí :(";
"lng_forward_confirm" = "¿Reenviar a {recipient}?";
"lng_forward_share_contact" = "¿Compartir contacto con {recipient}?";
"lng_forward_share_cant" = "No hay forma de compartir un contacto aquí :(";
"lng_forward_send_file_confirm" = "¿Enviar «{name}» a {recipient}?";
"lng_forward_send_files_confirm" = "¿Enviar los archivos seleccionados a {recipient}?";
"lng_forward_send_files_cant" = "No hay forma de enviar multimedia aquí :(";
"lng_forward_send" = "Enviar";
"lng_forward_messages" = "{count:_not_used_|Mensaje adjunto|# mensajes adjuntos}";
"lng_forwarding_from" = "{user} y {count:_not_used_|# otro|# otros}";
"lng_forward_messages#one" = "{count} mensaje reenviado";
"lng_forward_messages#other" = "{count} mensajes reenviados";
"lng_forwarding_from#one" = "{user} y {count} otro";
"lng_forwarding_from#other" = "{user} y {count} otros";
"lng_forwarding_from_two" = "{user} y {second_user}";
"lng_inline_switch_choose" = "Elige una conversación...";
"lng_inline_switch_cant" = "Lo sentimos, no puedes escribir aquí :(";
@@ -1016,6 +1131,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_contact_title" = "Editar nombre del contacto";
"lng_edit_channel_title" = "Editar canal";
"lng_edit_sign_messages" = "Firmar los mensajes";
"lng_edit_group_who_invites" = "Quién puede añadir miembros";
"lng_edit_group_invites_everybody" = "Todos los miembros";
"lng_edit_group_invites_only_admins" = "Sólo administradores";
"lng_edit_group" = "Editar grupo";
"lng_edit_self_title" = "Editar tu nombre";
"lng_confirm_contact_data" = "Nuevo contacto";
@@ -1040,15 +1158,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_selected_clear" = "Cancelar";
"lng_selected_delete" = "Eliminar";
"lng_selected_forward" = "Reenviar";
"lng_selected_count" = "{count:_not_used_|# mensaje|# mensajes}";
"lng_selected_count#one" = "{count} mensaje";
"lng_selected_count#other" = "{count} mensajes";
"lng_selected_cancel_sure_this" = "¿Detener el envío?";
"lng_selected_upload_stop" = "Detener";
"lng_selected_delete_sure_this" = "¿Quieres eliminar este mensaje?";
"lng_selected_delete_sure" = "¿Quieres eliminar {count:_not_used_|# mensaje|# mensajes}?";
"lng_selected_delete_sure#one" = "¿Quieres eliminar {count} mensaje?";
"lng_selected_delete_sure#other" = "¿Quieres eliminar {count} mensajes?";
"lng_delete_photo_sure" = "¿Quieres eliminar esta foto?";
"lng_delete_for_everyone_hint" = "Esto {count:_not_used_|lo|los} eliminará para todos en este chat.";
"lng_delete_for_me_chat_hint" = "Esto {count:_not_used_|lo|los} eliminará sólo para ti, y no para los otros participantes del chat.";
"lng_delete_for_me_hint" = "Esto {count:_not_used_|lo|los} eliminará sólo para ti.";
"lng_delete_for_everyone_hint#one" = "Esto lo eliminará para todos en este chat.";
"lng_delete_for_everyone_hint#other" = "Esto los eliminará para todos en este chat.";
"lng_delete_for_me_chat_hint#one" = "Esto lo eliminará sólo para ti, no para los otros miembros del chat.";
"lng_delete_for_me_chat_hint#other" = "Esto los eliminará sólo para ti, no para los otros miembros del chat.";
"lng_delete_for_me_hint#one" = "Esto lo eliminará sólo para ti.";
"lng_delete_for_me_hint#other" = "Esto los eliminará sólo para ti.";
"lng_delete_for_everyone_check" = "Eliminar para todos";
"lng_delete_for_other_check" = "Eliminar para {user}";
"lng_box_delete" = "Eliminar";
@@ -1060,7 +1183,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_about_text_3" = "Conoce más en las {faq_open}preguntas frecuentes de Telegram{faq_close}.";
"lng_about_done" = "Hecho";
"lng_search_found_results" = "{count:No se encontraron mensajes|# mensaje encontrado|# mensajes encontrados}";
"lng_search_no_results" = "No se encontraron mensajes";
"lng_search_found_results#one" = "{count} mensaje encontrado";
"lng_search_found_results#other" = "{count} mensajes encontrados";
"lng_search_global_results" = "Resultados de la búsqueda global";
"lng_media_save_progress" = "{ready} de {total} {mb}";
@@ -1169,11 +1294,133 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_player_message_yesterday" = "Ayer a las {time}";
"lng_player_message_date" = "{date} a las {time}";
"lng_rights_edit_admin" = "Editar administrador";
"lng_rights_edit_admin_header" = "¿Qué puede hacer este administrador?";
"lng_rights_about_add_admins_yes" = "Este administrador podrá añadir administradores con los mismos (o menos) permisos.";
"lng_rights_about_add_admins_no" = "Este administrador no podrá añadir administradores.";
"lng_rights_about_admin_cant_edit" = "No puedes editar los permisos de este administrador.";
"lng_rights_user_restrictions" = "Restricciones";
"lng_rights_user_restrictions_header" = "¿Qué puede hacer este usuario?";
"lng_rights_channel_info" = "Cambiar información del canal";
"lng_rights_channel_post" = "Publicar mensajes";
"lng_rights_channel_edit" = "Editar mensajes de otros";
"lng_rights_channel_delete" = "Eliminar mensajes de otros";
"lng_rights_group_info" = "Cambiar información del grupo";
"lng_rights_group_ban" = "Suspender usuarios";
"lng_rights_group_invite_link" = "Invitar con un enlace";
"lng_rights_group_invite" = "Añadir usuarios";
"lng_rights_group_pin" = "Anclar mensajes";
"lng_rights_group_delete" = "Eliminar mensajes";
"lng_rights_add_admins" = "Añadir administradores";
"lng_rights_chat_read" = "Leer mensajes";
"lng_rights_chat_send_text" = "Enviar mensajes";
"lng_rights_chat_send_media" = "Enviar multimedia";
"lng_rights_chat_send_stickers" = "Enviar stickers y GIF";
"lng_rights_chat_send_links" = "Enviar vista previa de enlaces";
"lng_rights_chat_banned_until_header" = "Restringido hasta";
"lng_rights_chat_banned_forever" = "Siempre";
"lng_rights_chat_banned_day#one" = "Por {count} día";
"lng_rights_chat_banned_day#other" = "Por {count} días";
"lng_rights_chat_banned_week#one" = "Por {count} semana";
"lng_rights_chat_banned_week#other" = "Por {count} semanas";
"lng_rights_chat_banned_custom" = "Personalizado";
"lng_rights_chat_banned_custom_date" = "Hasta el {date}";
"lng_rights_chat_banned_block" = "Suspender y eliminar del grupo";
"lng_restricted_send_message" = "Los administradores del grupo han restringido que escribas.";
"lng_restricted_send_media" = "Los administradores del grupo han restringido que envíes multimedia.";
"lng_restricted_send_stickers" = "Los administradores del grupo han restringido que envíes stickers.";
"lng_restricted_send_gifs" = "Los administradores del grupo han restringido que envíes GIF.";
"lng_restricted_send_inline" = "Los administradores del grupo han restringido que envíes contenido integrado.";
"lng_restricted_list_title" = "Usuarios restringidos";
"lng_banned_list_title" = "Usuarios suspendidos";
"lng_admin_log_title_all" = "Todas las acciones";
"lng_admin_log_title_selected" = "Acciones seleccionadas";
"lng_admin_log_filter" = "Filtrar";
"lng_admin_log_filter_title" = "Filtrar";
"lng_admin_log_filter_all_actions" = "Todas las acciones";
"lng_admin_log_filter_restrictions" = "Nuevas restricciones";
"lng_admin_log_filter_admins_new" = "Nuevos administradores";
"lng_admin_log_filter_members_new" = "Nuevos miembros";
"lng_admin_log_filter_info_group" = "Información del grupo";
"lng_admin_log_filter_info_channel" = "Información del canal";
"lng_admin_log_filter_messages_deleted" = "Mensajes eliminados";
"lng_admin_log_filter_messages_edited" = "Mensajes editados";
"lng_admin_log_filter_messages_pinned" = "Mensajes anclados";
"lng_admin_log_filter_members_removed" = "Miembros eliminados";
"lng_admin_log_filter_all_admins" = "Todos los usuarios";
"lng_admin_log_about" = "¿Qué es esto?";
"lng_admin_log_about_text" = "Esta es una lista de las acciones de servicio realizadas por los miembros y administradores del grupo en las últimas 48 horas.";
"lng_admin_log_no_results_title" = "No se encontraron acciones";
"lng_admin_log_no_results_text" = "No se encontraron acciones que coincidan con tu solicitud.";
"lng_admin_log_no_results_search_text" = "No se encontraron acciones que contengan “{query}”.";
"lng_admin_log_no_events_title" = "Aún no hay acciones";
"lng_admin_log_no_events_text" = "Los miembros y administradores\nno han realizado acciones de \nservicio en las últimas 48 horas.";
"lng_admin_log_empty_text" = "Vacío";
"lng_admin_log_changed_title_group" = "{from} cambió el nombre del grupo a «{title}»";
"lng_admin_log_changed_title_channel" = "{from} cambió el nombre del canal a «{title}»";
"lng_admin_log_changed_description_group" = "{from} editó la descripción del grupo:";
"lng_admin_log_removed_description_group" = "{from} eliminó la descripción del grupo";
"lng_admin_log_changed_description_channel" = "{from} editó la descripción del canal:";
"lng_admin_log_removed_description_channel" = "{from} eliminó la descripción del canal";
"lng_admin_log_previous_description" = "Descripción anterior";
"lng_admin_log_changed_link_group" = "{from} cambió el enlace del grupo:";
"lng_admin_log_removed_link_group" = "{from} eliminó el enlace del grupo";
"lng_admin_log_changed_link_channel" = "{from} cambió el enlace del canal:";
"lng_admin_log_removed_link_channel" = "{from} eliminó el enlace del canal";
"lng_admin_log_previous_link" = "Enlace previo";
"lng_admin_log_changed_photo_group" = "{from} cambió la foto de grupo";
"lng_admin_log_changed_photo_channel" = "{from} cambió la foto del canal";
"lng_admin_log_removed_photo_group" = "{from} eliminó la foto del grupo";
"lng_admin_log_removed_photo_channel" = "{from} eliminó la foto del canal";
"lng_admin_log_invites_enabled" = "{from} permitió las invitaciones";
"lng_admin_log_invites_disabled" = "{from} desactivó las invitaciones";
"lng_admin_log_signatures_enabled" = "{from} activó las firmas";
"lng_admin_log_signatures_disabled" = "{from} desactivó las firmas";
"lng_admin_log_pinned_message" = "{from} ancló un mensaje:";
"lng_admin_log_unpinned_message" = "{from} desancló un mensaje";
"lng_admin_log_edited_caption" = "{from} editó un comentario:";
"lng_admin_log_removed_caption" = "{from} eliminó un comentario";
"lng_admin_log_previous_caption" = "Comentario original";
"lng_admin_log_edited_message" = "{from} editó un mensaje:";
"lng_admin_log_previous_message" = "Mensaje original";
"lng_admin_log_deleted_message" = "{from} eliminó un mensaje:";
"lng_admin_log_participant_joined" = "{from} se unió al grupo";
"lng_admin_log_participant_joined_channel" = "{from} se unió al canal";
"lng_admin_log_participant_left" = "{from} dejó el grupo";
"lng_admin_log_participant_left_channel" = "{from} dejó el canal";
"lng_admin_log_invited" = "invitó a {user}";
"lng_admin_log_banned" = "suspendió a {user}";
"lng_admin_log_restricted" = "cambió las restricciones de {user} {until}";
"lng_admin_log_promoted" = "cambió los privilegios de {user}";
"lng_admin_log_user_with_username" = "{name} ({mention})";
"lng_admin_log_restricted_forever" = "indefinidamente";
"lng_admin_log_restricted_until" = "hasta el {date}";
"lng_admin_log_banned_view_messages" = "Leer mensajes";
"lng_admin_log_banned_send_messages" = "Enviar mensajes";
"lng_admin_log_banned_send_media" = "Enviar multimedia";
"lng_admin_log_banned_send_stickers" = "Enviar stickers y GIF";
"lng_admin_log_banned_embed_links" = "Enviar vista previa de enlaces";
"lng_admin_log_admin_change_info" = "Cambiar información";
"lng_admin_log_admin_post_messages" = "Publicar mensajes";
"lng_admin_log_admin_edit_messages" = "Editar mensajes";
"lng_admin_log_admin_delete_messages" = "Eliminar mensajes";
"lng_admin_log_admin_ban_users" = "Suspender usuarios";
"lng_admin_log_admin_invite_users" = "Añadir usuarios";
"lng_admin_log_admin_invite_link" = "Invitar con un enlace";
"lng_admin_log_admin_pin_messages" = "Anclar mensajes";
"lng_admin_log_admin_add_admins" = "Añadir administradores";
// Not used
"lng_topbar_info" = "Información";
"lng_profile_group_info" = "Información";
"lng_profile_channel_info" = "Información";
"lng_channel_add_admins" = "Nuevo administrador";
// Wnd specific

View File

@@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_menu_update" = "Aggiorna";
"lng_menu_restart" = "Riavvia";
"lng_menu_back" = "Indietro";
"lng_menu_night_mode" = "Modalità notte";
"lng_disable_notifications_from_tray" = "Disattiva notifiche";
"lng_enable_notifications_from_tray" = "Attiva notifiche";
@@ -88,7 +89,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_continue" = "Continua";
"lng_close" = "Chiudi";
"lng_connecting" = "Connetto...";
"lng_reconnecting" = "Riconnetto {count:ora|tra # s|tra # s}...";
"lng_connecting_to_proxy" = "Connetto al proxy...";
"lng_connecting_settings" = "Impostazioni";
"lng_reconnecting#one" = "Riconnetto tra {count} s...";
"lng_reconnecting#other" = "Riconnetto tra {count} s...";
"lng_reconnecting_try_now" = "Prova ora";
"lng_status_service_notifications" = "notifiche di servizio";
@@ -102,8 +106,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_last_month" = "ultimo accesso entro un mese";
"lng_status_invisible" = "invisibile";
"lng_status_lastseen_now" = "ultimo accesso adesso";
"lng_status_lastseen_minutes" = "ultimo accesso {count:_not_used_|# minuto|# minuti} fa";
"lng_status_lastseen_hours" = "ultimo accesso {count:_not_used_|# ora|# ore} fa";
"lng_status_lastseen_minutes#one" = "ultimo accesso {count} minuto fa";
"lng_status_lastseen_minutes#other" = "ultimo accesso {count} minuti fa";
"lng_status_lastseen_hours#one" = "ultimo accesso {count} ora fa";
"lng_status_lastseen_hours#other" = "ultimo accesso {count} ore fa";
"lng_status_lastseen_today" = "ultimo accesso oggi alle {time}";
"lng_status_lastseen_yesterday" = "ultimo accesso ieri alle {time}";
"lng_status_lastseen_date" = "ultimo accesso {date}";
@@ -112,24 +118,43 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_connecting" = "connetto...";
"lng_chat_status_unaccessible" = "gruppo inaccessibile";
"lng_chat_status_members" = "{count:nessun membro|# membro|# membri}";
"lng_chat_status_members_online" = "{count:_not_used_|# membro|# membri}, {count_online:_not_used_|# in linea|# in linea}";
"lng_chat_status_no_members" = "nessun membro";
"lng_chat_status_members#one" = "{count} membro";
"lng_chat_status_members#other" = "{count} membri";
"lng_chat_status_online#one" = "{count} in linea";
"lng_chat_status_online#other" = "{count} in linea";
"lng_chat_status_members_online" = "{members_count}, {online_count}";
"lng_channel_status" = "canale";
"lng_group_status" = "gruppo";
"lng_channel_members_link" = "{count:_not_used_|# membro|# membri}";
"lng_channel_admins_link" = "{count:_not_used_|# amministratore|# amministratori}";
"lng_channel_members_link#one" = "{count} membro";
"lng_channel_members_link#other" = "{count} membri";
"lng_channel_admins_link#one" = "{count} amministratore";
"lng_channel_admins_link#other" = "{count} amministratori";
"lng_server_error" = "Errore interno del server.";
"lng_flood_error" = "Troppi tentativi. Per favore riprova più tardi.";
"lng_gif_error" = "C'è stato un errore nel leggere la GIF :(";
"lng_gif_error" = "Si è verificato un errore nel leggere la GIF :(";
"lng_edit_error" = "Non puoi modificare questo messaggio";
"lng_join_channel_error" = "Spiacenti, ti sei unito a troppi canali e supergruppi. Per favore lasciane qualcuno prima di unirti.";
"lng_error_phone_flood" = "Spiacenti, hai eliminato e ricreato il tuo account troppe volte di recente. Per favore attendi alcuni giorni prima di iscriverti di nuovo.";
"lng_error_start_minimized_passcoded" = "Hai inserito un codice di blocco, quindi l'app non può essere avviata minimizzata. L'app ti chiederà il codice prima di potersi avviare.";
"lng_error_pinned_max" = "Spiacenti, non puoi fissare in alto più di {count:_not_used_|# chat|# chat}.";
"lng_error_pinned_max#one" = "Spiacenti, non puoi fissare in alto più di {count} chat.";
"lng_error_pinned_max#other" = "Spiacenti, non puoi fissare in alto più di {count} chat.";
"lng_error_public_groups_denied" = "Sfortunatamente, non ti è permesso partecipare ai gruppi pubblici.\n{more_info}";
"lng_error_cant_edit_admin" = "Spiacenti, non puoi modificare le autorizzazioni di questo amministratore.";
"lng_error_cant_add_member" = "Spiacenti, non puoi aggiungere il bot a questo gruppo. Chiedi ad un amministratore di farlo.";
"lng_error_cant_add_bot" = "Spiacenti, questo bot non può essere aggiunto ai gruppi.";
"lng_error_cant_add_admin_invite" = "Spiacenti, non puoi aggiungere questo utente come amministratore perché non è un membro del gruppo e non sei autorizzato ad invitarlo.";
"lng_error_cant_add_admin_unban" = "Spiacenti, non puoi aggiungere questo utente come amministratore perché è bloccato e non puoi sbloccarlo.";
"lng_error_cant_ban_admin" = "Spiacenti, non puoi bloccare questo utente perché è un amministratore in questo gruppo e non sei autorizzato a degradarlo.";
"lng_sure_add_admin_invite" = "Questo utente non è membro del gruppo. Vuoi aggiungerlo e promuoverlo ad amministratore?";
"lng_sure_add_admin_unban" = "Questo utente è limitato o bloccato nel gruppo. Sei sicuro di volerlo sbloccare e promuoverlo?";
"lng_sure_ban_admin" = "Questo utente è un amministratore nel gruppo. Sei sicuro di voler proseguire e limitarlo?";
"lng_sure_ban_user_group" = "Bloccare {user} nel gruppo?";
"lng_sure_enable_socks" = "Sei sicuro di voler attivare questo proxy?\n\nServer: {server}\nPorta: {port}\n\nPuoi cambiare il tuo server proxy più tardi nelle Impostazioni (Tipo di connessione).";
"lng_sure_enable" = "Attiva";
"lng_edit_deleted" = "Questo messaggio è stato eliminato";
"lng_edit_too_long" = "Il tuo messaggio è troppo lungo";
@@ -198,9 +223,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_signin_sure_reset" = "Attenzione!\n\nPerderai tutte le chat e i messaggi, insieme a tutti i media e i file condivisi!\n\nVuoi ripristinare il tuo account?";
"lng_signin_reset" = "Ripristina";
"lng_signin_reset_wait" = "Dato che l'account {phone_number} è attivo e protetto da una password, lo elimineremo tra 1 settimana per motivi di sicurezza. Puoi annullare questo processo in qualsiasi momento.\n\nPotrai ripristinare il tuo account tra:\n{when}";
"lng_signin_reset_in_days" = "{count_days:0 giorni|# giorno|# giorni} {count_hours:0 ore|# ora|# ore} {count_minutes:0 minuti|# minuto|# minuti}";
"lng_signin_reset_in_hours" = "{count_hours:0 ore|# ora|# ore} {count_minutes:0 minuti|# minuto|# minuti}";
"lng_signin_reset_in_minutes" = "{count_minutes:0 minuti|# minuto|# minuti}";
"lng_signin_reset_days#one" = "{count} giorno";
"lng_signin_reset_days#other" = "{count} giorni";
"lng_signin_reset_hours#one" = "{count} ora";
"lng_signin_reset_hours#other" = "{count} ore";
"lng_signin_reset_minutes#one" = "{count} minuto";
"lng_signin_reset_minutes#other" = "{count} minuti";
"lng_signin_reset_in_days" = "{days_count} {hours_count} {minutes_count}";
"lng_signin_reset_in_hours" = "{hours_count} {minutes_count}";
"lng_signin_reset_cancelled" = "I tuoi tentativi recenti di ripristinare questo account sono stati annullati dal suo utente attivo. Per favore riprova tra 7 giorni.";
"lng_signup_title" = "Le tue info";
@@ -223,7 +253,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_settings_save" = "Salva";
"lng_settings_upload" = "Imposta foto profilo";
"lng_settings_crop_profile" = "Seleziona un riquadro per la tua foto profilo";
"lng_settings_crop_profile" = "Seleziona un'area per la tua foto profilo";
"lng_settings_uploading_photo" = "Carico foto...";
"lng_settings_edit" = "Modifica";
"lng_settings_drop_area_subtitle" = "per impostarla come foto profilo";
@@ -241,10 +271,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_username_link" = "Questo link apre una chat con te:";
"lng_username_copied" = "Link copiato negli appunti.";
"lng_bio_title" = "Modifica la tua bio";
"lng_bio_placeholder" = "Bio";
"lng_bio_about" = "Puoi aggiungere qualche riga su di te. Chiunque apra il tuo profilo vedrà questo testo.";
"lng_settings_section_info" = "Info";
"lng_settings_phone_number" = "Numero di telefono:";
"lng_settings_username" = "Username:";
"lng_settings_choose_username" = "Scegli username";
"lng_settings_empty_bio" = "Nessuna";
"lng_settings_section_notify" = "Notifiche";
"lng_settings_desktop_notify" = "Notifiche desktop";
@@ -303,7 +338,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_backgrounds_header" = "Scegli un nuovo sfondo per la chat";
"lng_theme_sure_keep" = "Mantenere questo tema?";
"lng_theme_reverting" = "Ripristino il vecchio tema tra {count:_not_used_|# secondo|# secondi}.";
"lng_theme_reverting#one" = "Ripristino il vecchio tema tra {count} secondo.";
"lng_theme_reverting#other" = "Ripristino il vecchio tema tra {count} secondi.";
"lng_theme_keep_changes" = "Mantieni modifiche";
"lng_theme_revert" = "Ripristina";
@@ -329,14 +365,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_local_storage_title" = "Archivio locale";
"lng_settings_no_data_cached" = "Non ci sono dati nella cache!";
"lng_settings_images_cached" = "{count:_not_used_|# immagine|# immagini}, {size}";
"lng_settings_audios_cached" = "{count:_not_used_|# messaggio vocale|# messaggi vocali}, {size}";
"lng_settings_images_cached#one" = "{count} immagine, {size}";
"lng_settings_images_cached#other" = "{count} immagini, {size}";
"lng_settings_audios_cached#one" = "{count} messaggio vocale, {size}";
"lng_settings_audios_cached#other" = "{count} messaggi vocali, {size}";
"lng_local_storage_clear" = "Elimina tutto";
"lng_local_storage_clearing" = "Elimino...";
"lng_local_storage_cleared" = "Eliminato!";
"lng_local_storage_clear_failed" = "Eliminazione fallita :(";
"lng_settings_section_advanced_settings" = "Impostazioni avanzate";
"lng_settings_enable_night_theme" = "Attiva la modalità notte";
"lng_settings_disable_night_theme" = "Disattiva la modalità notte";
"lng_passcode_remove_button" = "Rimuovi";
@@ -348,8 +388,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_passcode_autolock" = "Blocco automatico";
"lng_passcode_autolock_away" = "Blocco automatico se lontano per:";
"lng_passcode_autolock_inactive" = "Blocco automatico se inattivo per:";
"lng_passcode_autolock_minutes" = "{count:_not_used_|# minuto|# minuti}";
"lng_passcode_autolock_hours" = "{count:_not_used_|# ora|# ore}";
"lng_passcode_autolock_minutes#one" = "{count} minuto";
"lng_passcode_autolock_minutes#other" = "{count} minuti";
"lng_passcode_autolock_hours#one" = "{count} ora";
"lng_passcode_autolock_hours#other" = "{count} ore";
"lng_passcode_enter_old" = "Inserisci il codice attuale";
"lng_passcode_enter_first" = "Inserisci un codice";
"lng_passcode_enter_new" = "Inserisci il nuovo codice";
@@ -419,7 +461,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_settings_reset_one_sure" = "Vuoi terminare questa sessione?";
"lng_settings_reset_button" = "Chiudi";
"lng_settings_reset_done" = "Altre sessioni terminate";
"lng_settings_manage_local_storage" = "Gestisci archivio locale";
"lng_settings_manage_local_storage" = "Gestisci l'archivio locale";
"lng_settings_ask_question" = "Fai una domanda";
"lng_settings_ask_sure" = "Per favore nota che l'assistenza di Telegram è fornita da volontari. Proviamo a rispondere quanto prima, ma potrebbe volerci del tempo.\n\nDai un'occhiata alle domande frequenti di Telegram: contengono suggerimenti importanti per risolvere i problemi e risposte a quasi tutte le domande.";
"lng_settings_faq_button" = "Domande frequenti";
@@ -455,32 +497,46 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_privacy_lastseen_title" = "Ultimo accesso";
"lng_edit_privacy_lastseen_description" = "Puoi decidere chi può vedere il tuo ultimo accesso:";
"lng_edit_privacy_lastseen_warning" = "Importante: non potrai vedere l'ultimo accesso delle persone con cui non condividi l'ultimo accesso. Verrà mostrato un orario approssimativo (di recente, entro una settimana, entro un mese).";
"lng_edit_privacy_lastseen_always" = "Condividi con{count:| # utente| # utenti}";
"lng_edit_privacy_lastseen_never" = "Non condividere con{count:| # utente| # utenti}";
"lng_edit_privacy_lastseen_always_empty" = "Condividi con";
"lng_edit_privacy_lastseen_always#one" = "Condividi con {count} utente";
"lng_edit_privacy_lastseen_always#other" = "Condividi con {count} utenti";
"lng_edit_privacy_lastseen_never_empty" = "Non condividere con";
"lng_edit_privacy_lastseen_never#one" = "Non condividere con {count} utente";
"lng_edit_privacy_lastseen_never#other" = "Non condividere con {count} utenti";
"lng_edit_privacy_lastseen_exceptions" = "Queste impostazioni annulleranno i valori precedenti.";
"lng_edit_privacy_lastseen_always_title" = "Condividi con";
"lng_edit_privacy_lastseen_never_title" = "Non condividere co";
"lng_edit_privacy_groups_title" = "Gruppi";
"lng_edit_privacy_groups_description" = "Puoi decidere chi può aggiungerti a gruppi e canali con precisione granulare:";
"lng_edit_privacy_groups_always" = "Consenti sempre{count:| # utente| # utenti}";
"lng_edit_privacy_groups_never" = "Non consentire mai{count:| # utente| # utenti}";
"lng_edit_privacy_groups_always_empty" = "Consenti sempre";
"lng_edit_privacy_groups_always#one" = "Consenti sempre {count} utente";
"lng_edit_privacy_groups_always#other" = "Consenti sempre {count} utenti";
"lng_edit_privacy_groups_never_empty" = "Non consentire mai";
"lng_edit_privacy_groups_never#one" = "Non consentire mai {count} utente";
"lng_edit_privacy_groups_never#other" = "Non consentire mai {count} utenti";
"lng_edit_privacy_groups_exceptions" = "Questi utenti potranno o non potranno aggiungerti a gruppi e canali indipendentemente dalle impostazioni precedenti.";
"lng_edit_privacy_groups_always_title" = "Consenti sempre";
"lng_edit_privacy_groups_never_title" = "Non consentire mai";
"lng_edit_privacy_calls_title" = "Privacy chiamate";
"lng_edit_privacy_calls_description" = "Puoi decidere chi può chiamarti:";
"lng_edit_privacy_calls_always" = "Consenti sempre{count:| # utente| # utenti}";
"lng_edit_privacy_calls_never" = "Non consentire mai{count:| # utente| # utenti}";
"lng_edit_privacy_calls_always_empty" = "Consenti sempre";
"lng_edit_privacy_calls_always#one" = "Consenti sempre {count} utente";
"lng_edit_privacy_calls_always#other" = "Consenti sempre {count} utenti";
"lng_edit_privacy_calls_never_empty" = "Non consentire mai";
"lng_edit_privacy_calls_never#one" = "Non consentire mai {count} utente";
"lng_edit_privacy_calls_never#other" = "Non consentire mai {count} utenti";
"lng_edit_privacy_calls_exceptions" = "Questi utenti potranno o non potranno chiamarti indipendentemente dalle impostazioni precedenti.";
"lng_edit_privacy_calls_always_title" = "Consenti sempre";
"lng_edit_privacy_calls_never_title" = "Non consentire mai";
"lng_self_destruct_title" = "Autodistruzione account";
"lng_self_destruct_description" = "Se non ti connetti almeno una volta in questo periodo, il tuo account verrà eliminato insieme a tutti i gruppi, i messaggi e i contatti.";
"lng_self_destruct_months" = "{count:_not_used_|# mese|# mesi}";
"lng_self_destruct_years" = "{count:_not_used_|# anno|# anni}";
"lng_self_destruct_months#one" = "{count} mese";
"lng_self_destruct_months#other" = "{count} mesi";
"lng_self_destruct_years#one" = "{count} anno";
"lng_self_destruct_years#other" = "{count} anni";
"lng_change_phone_title" = "Cambia numero di telefono";
"lng_change_phone_description" = "Puoi cambiare il tuo numero di telefono\nqui. Il tuo account e tutti i tuoi dati cloud\n— messaggi, file, contatti, etc. saranno\ntrasferiti sul nuovo numero.\n\n[b]Importante[/b]: a tutti i tuoi contatti di Telegram\nverrà aggiunto il tuo [b]nuovo numero[/b] ai contatti,\npurché abbiano il tuo vecchio numero e\ntu non li abbia bloccati su Telegram.";
@@ -506,15 +562,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_invite_link_section" = "Link d'invito";
"lng_profile_create_public_link" = "Crea link pubblico";
"lng_profile_edit_public_link" = "Modifica link pubblico";
"lng_profile_manage_admins" = "Gestisci amministratori";
"lng_profile_manage_blocklist" = "Gestisci utenti bloccati";
"lng_profile_common_groups" = "{count:_not_used_|# gruppo|# gruppi} in comune";
"lng_profile_search_members" = "Cerca membri";
"lng_profile_manage_admins" = "Gestisci gli amministratori";
"lng_profile_manage_blocklist" = "Gestisci gli utenti bloccati";
"lng_profile_manage_restrictedlist" = "Gestisci gli utenti limitati";
"lng_profile_recent_actions" = "Azioni recenti";
"lng_profile_common_groups#one" = "{count} gruppo in comune";
"lng_profile_common_groups#other" = "{count} gruppi in comune";
"lng_profile_common_groups_section" = "Gruppi in comune";
"lng_profile_participants_section" = "Membri";
"lng_profile_info_section" = "Info";
"lng_profile_mobile_number" = "Cellulare:";
"lng_profile_username" = "Username:";
"lng_profile_link" = "Link:";
"lng_profile_bio" = "Bio:";
"lng_profile_add_contact" = "Aggiungi contatto";
"lng_profile_edit_contact" = "Modifica";
"lng_profile_enable_notifications" = "Notifiche";
@@ -542,23 +603,30 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_delete_and_exit" = "Esci";
"lng_profile_kick" = "Rimuovi";
"lng_profile_admin" = "amministratore";
"lng_profile_edit_permissions" = "Modifica";
"lng_profile_sure_kick" = "Rimuovere {user} dal gruppo?";
"lng_profile_sure_kick_channel" = "Rimuovere {user} dal canale?";
"lng_profile_sure_kick_admin" = "Rimuovere {user} dagli amministratori?";
"lng_profile_loading" = "Carico...";
"lng_profile_shared_media" = "Media condivisi";
"lng_profile_no_media" = "Nessun media in questa chat.";
"lng_profile_photos" = "{count:_not_used_|# foto|# foto}";
"lng_profile_photos#one" = "{count} foto";
"lng_profile_photos#other" = "{count} foto";
"lng_profile_photos_header" = "Foto";
"lng_profile_videos" = "{count:_not_used_|# video|# video}";
"lng_profile_videos#one" = "{count} video";
"lng_profile_videos#other" = "{count} video";
"lng_profile_videos_header" = "Video";
"lng_profile_songs" = "{count:_not_used_|# file audio|# file audio}";
"lng_profile_songs#one" = "{count} file audio";
"lng_profile_songs#other" = "{count} file audio";
"lng_profile_songs_header" = "File audio";
"lng_profile_files" = "{count:_not_used_|# file|# file}";
"lng_profile_files#one" = "{count} file";
"lng_profile_files#other" = "{count} file";
"lng_profile_files_header" = "File";
"lng_profile_audios" = "{count:_not_used_|# messaggio vocale|# messaggi vocali}";
"lng_profile_audios#one" = "{count} messaggio vocale";
"lng_profile_audios#other" = "{count} messaggi vocali";
"lng_profile_audios_header" = "Messaggi vocali";
"lng_profile_shared_links" = "{count:_not_used_|# link condiviso|# link condivisi}";
"lng_profile_shared_links#one" = "{count} link condiviso";
"lng_profile_shared_links#other" = "{count} link condivisi";
"lng_profile_shared_links_header" = "Link condivisi";
"lng_profile_copy_phone" = "Copia numero di telefono";
"lng_profile_copy_fullname" = "Copia nome";
@@ -578,16 +646,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_report_button" = "Segnala";
"lng_report_thanks" = "Grazie! La tua segnalazione sarà presto esaminata dal nostro team.";
"lng_channel_add_admins" = "Nuovo amministratore";
"lng_channel_add_members" = "Aggiungi membri";
"lng_channel_add_banned" = "Blocca utente";
"lng_channel_add_restricted" = "Limita utente";
"lng_channel_members" = "Membri";
"lng_channel_only_last_shown" = "Solo {count:_not_used_|l'ultimo membro è mostrato|gli ultimi # membri sono mostrati} qui";
"lng_channel_only_last_shown#one" = "Solo l'ultimo membro è mostrato qui";
"lng_channel_only_last_shown#other" = "Solo gli ultimi {count} membri sono mostrati qui";
"lng_channel_admins" = "Amministratori";
"lng_channel_add_admin" = "Aggiungi amministratore";
"lng_channel_admin_sure" = "Aggiungere {user} agli amministratori?";
"lng_channel_admins_too_much" = "Spiacenti, hai raggiunto il limite di amministratori. Prima devi rimuoverne qualcuno.";
"lng_channel_admin_status_creator" = "Creatore";
"lng_channel_admin_status_promoted_by" = "Promosso da {user}";
"lng_channel_admin_status_not_admin" = "Non amministratore";
"lng_group_blocked_list_about" = "Gli utenti bloccati sono rimossi dal gruppo\ne possono rientrare solo se\ninvitati da un amministratore.\nI link di invito non funzionano per loro.";
"lng_group_blocked_list_about" = "Gli utenti bloccati sono rimossi dal gruppo e possono rientrare solo se invitati da un amministratore.\nI link di invito non funzionano per loro.";
"lng_chat_all_members_admins" = "Tutti sono amministratori";
"lng_chat_about_all_admins" = "Tutti i membri possono aggiungere nuovi membri, modificare nome e foto del gruppo.";
@@ -595,7 +667,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_participant_filter" = "Cerca";
"lng_participant_invite" = "Invita";
"lng_participant_invite_sorry" = "Spiacenti, puoi aggiungere solo {count:_not_used|il primo membro|i primi # membri} a un canale.\n\nDa adesso, le persone potranno unirsi tramite il tuo link d'invito.";
"lng_participant_invite_sorry#one" = "Spiacenti, puoi aggiungere solo il primo membro a un canale.\n\nDa adesso, le persone potranno unirsi tramite il tuo link d'invito.";
"lng_participant_invite_sorry#other" = "Spiacenti, puoi aggiungere solo i primi {count} membri a un canale.\n\nDa adesso, le persone potranno unirsi tramite il tuo link d'invito.";
"lng_create_group_back" = "Indietro";
"lng_create_group_next" = "Avanti";
"lng_create_group_create" = "Crea";
@@ -618,8 +691,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_create_channel_link_available" = "Questo link è disponibile";
"lng_create_channel_link_copied" = "Link copiato negli appunti";
"lng_create_group_crop" = "Seleziona un riquadro per la foto del gruppo";
"lng_create_channel_crop" = "Seleziona un riquadro per la foto del canale";
"lng_create_group_crop" = "Seleziona un'area per la foto del gruppo";
"lng_create_channel_crop" = "Seleziona un'area per la foto del canale";
"lng_failed_add_participant" = "Impossibile aggiungere l'utente. Riprova più tardi.";
"lng_failed_add_not_mutual" = "Spiacenti, se una persona lascia un gruppo, solo un contatto reciproco può aggiungerla (chi ti invita deve avere il tuo numero, e viceversa).";
@@ -637,8 +710,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_message_empty" = "Messaggio vuoto";
"lng_message_unsupported" = "Questo messaggio non è supportato dalla tua versione di Telegram Desktop. Per favore, aggiorna all'ultima versione dalle Impostazioni o installalo da {link}";
"lng_duration_seconds" = "{count:_not_used_|# secondo|# secondi}";
"lng_duration_minutes_seconds" = "{count_minutes:# min|# min|# min} {count_seconds:# sec|# sec|# sec}";
"lng_duration_seconds#one" = "{count} secondo";
"lng_duration_seconds#other" = "{count} secondi";
"lng_duration_minsec_minutes#one" = "{count} min";
"lng_duration_minsec_minutes#other" = "{count} min";
"lng_duration_minsec_seconds#one" = "{count} sec";
"lng_duration_minsec_seconds#other" = "{count} sec";
"lng_duration_minutes_seconds" = "{minutes_count} {seconds_count}";
"lng_action_add_user" = "{from} ha aggiunto {user}";
"lng_action_add_users_many" = "{from} ha aggiunto {users}";
@@ -676,18 +754,33 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_action_pinned_media_sticker" = "uno sticker";
"lng_action_pinned_media_emoji_sticker" = "uno {emoji} sticker";
"lng_action_pinned_media_game" = "il gioco «{game}»";
"lng_action_game_score" = "{from} ha totalizzato {count:# punti|# punto|# punti} a {game}";
"lng_action_game_you_scored" = "Hai totalizzato {count:# punti|# punto|# punti} a {game}";
"lng_action_game_score_no_game" = "{from} ha totalizzato {count:# punti|# punto|# punti}";
"lng_action_game_you_scored_no_game" = "Hai totalizzato {count:# punti|# punto|# punti}";
"lng_action_game_score#one" = "{from} ha totalizzato {count} punto a {game}";
"lng_action_game_score#other" = "{from} ha totalizzato {count} punti a {game}";
"lng_action_game_you_scored#one" = "Hai totalizzato {count} punto a {game}";
"lng_action_game_you_scored#other" = "Hai totalizzato {count} punti a {game}";
"lng_action_game_score_no_game#one" = "{from} ha totalizzato {count} punto";
"lng_action_game_score_no_game#other" = "{from} ha totalizzato {count} punti";
"lng_action_game_you_scored_no_game#one" = "Hai totalizzato {count} punto";
"lng_action_game_you_scored_no_game#other" = "Hai totalizzato {count} punti";
"lng_action_payment_done" = "Hai appena trasferito con successo {amount} a {user}";
"lng_action_payment_done_for" = "Hai appena trasferito con successo {amount} a {user} per {invoice}";
"lng_action_took_screenshot" = "{from} ha fatto uno screenshot!";
"lng_action_you_took_screenshot" = "Hai fatto uno screenshot!";
"lng_profile_migrate_reached" = "Limite di {count:_not_used_|# membro|# membri} raggiunto";
"lng_ttl_photo_received" = "{from} ti inviato una foto con autodistruzione. Per favore visualizzala sul tuo cellulare.";
"lng_ttl_photo_sent" = "Hai inviato una foto con autodistruzione.";
"lng_ttl_photo_expired" = "La foto è scaduta";
"lng_ttl_video_received" = "{from} ti inviato un video con autodistruzione. Per favore visualizzalo sul tuo cellulare.";
"lng_ttl_video_sent" = "Hai inviato un video con autodistruzione.";
"lng_ttl_video_expired" = "Il video è scaduto";
"lng_profile_migrate_reached#one" = "Limite di {count} membro raggiunto";
"lng_profile_migrate_reached#other" = "Limite di {count} membri raggiunto";
"lng_profile_migrate_body" = "Per superare questo limite, puoi aggiornare il gruppo a supergruppo.";
"lng_profile_migrate_learn_more" = "Scopri di più »";
"lng_profile_migrate_about" = "Se vuoi superare questo limite, puoi aggiornare il gruppo a supergruppo. Nei supergruppi:";
"lng_profile_migrate_feature1" = "— I supergruppi hanno massimo {count:_not_used_|# membro|# membri}";
"lng_profile_migrate_feature1#one" = "— I supergruppi hanno massimo {count} membro";
"lng_profile_migrate_feature1#other" = "— I supergruppi hanno massimo {count} membri";
"lng_profile_migrate_feature2" = "— I nuovi membri vedono tutta la cronologia";
"lng_profile_migrate_feature3" = "— Gli admin eliminano i messaggi per tutti";
"lng_profile_migrate_feature4" = "— Le notifiche sono disattivate di default";
@@ -702,7 +795,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_convert_feature4" = "— Il creatore può creare un link pubblico per il gruppo";
"lng_profile_convert_warning" = "{bold_start}Nota:{bold_end} Questa azione non può essere annullata";
"lng_profile_convert_confirm" = "Converti";
"lng_profile_add_more_after_upgrade" = "Potrai aggiungere fino a {count:_not_used_|# membro|# membri} dopo aver aggiornato a supergruppo.";
"lng_profile_add_more_after_upgrade#one" = "Potrai aggiungere fino a {count} membro dopo aver aggiornato il tuo gruppo a supergruppo.";
"lng_profile_add_more_after_upgrade#other" = "Potrai aggiungere fino a {count} membri dopo aver aggiornato il tuo gruppo a supergruppo.";
"lng_channel_not_accessible" = "Spiacenti, questo canale non è accessibile.";
"lng_group_not_accessible" = "Spiacenti, questo gruppo non è accessibile.";
@@ -717,7 +811,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_group_invite_want_join_channel" = "Vuoi unirti al canale «{title}»?";
"lng_group_invite_join" = "Unisciti";
"lng_group_invite_members" = "{count:_not_used_|# membro|# membri}, tra cui:";
"lng_group_invite_members#one" = "{count} membro, tra cui:";
"lng_group_invite_members#other" = "{count} membri, tra cui:";
"lng_group_invite_link" = "Link d'invito:";
"lng_group_invite_create" = "Crea un link d'invito";
@@ -730,6 +825,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_channel_public_link_copied" = "Link copiato negli appunti.";
"lng_forwarded" = "Inoltrato da {user}";
"lng_forwarded_date" = "Originale: {date}";
"lng_forwarded_channel" = "Inoltrato da {channel}";
"lng_forwarded_via" = "Inoltrato da {user} via {inline_bot}";
"lng_forwarded_channel_via" = "Inoltrato da {channel} via {inline_bot}";
@@ -809,7 +905,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_stickers_you_have" = "Organizza e riordina i set di sticker";
"lng_stickers_featured" = "Sticker in primo piano";
"lng_stickers_return" = "Annulla";
"lng_stickers_count" = "{count:Carico...|# sticker|# sticker}";
"lng_stickers_count#one" = "{count} sticker";
"lng_stickers_count#other" = "{count} sticker";
"lng_stickers_masks_pack" = "Questo è un set di maschere. Puoi usarle nell'editor fotografico sulle nostre app mobili.";
"lng_in_dlg_photo" = "Foto";
@@ -839,6 +936,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_cant_invite_banned" = "Spiacenti, solo l'amministratore può aggiungere questo utente.";
"lng_cant_invite_privacy" = "Spiacenti, non puoi aggiungere questo utente ai gruppi a causa delle sue impostazioni di privacy.";
"lng_cant_invite_privacy_channel" = "Spiacenti, non puoi aggiungere questo utente ai canali a causa delle sue impostazioni di privacy.";
"lng_cant_delete_group#one" = "Spiacenti, al momento non è possibile eliminare manualmente gruppi con più di {count} membro. Per favore contatta l'assistenza di Telegram se vuoi eliminare questo gruppo.";
"lng_cant_delete_group#other" = "Spiacenti, al momento non è possibile eliminare manualmente gruppi con più di {count} membri. Per favore contatta l'assistenza di Telegram se vuoi eliminare questo gruppo.";
"lng_cant_delete_channel#one" = "Spiacenti, al momento non è possibile eliminare manualmente canali con più di {count} membro. Per favore contatta l'assistenza di Telegram se vuoi eliminare questo canale.";
"lng_cant_delete_channel#other" = "Spiacenti, al momento non è possibile eliminare manualmente canali con più di {count} membri. Per favore contatta l'assistenza di Telegram se vuoi eliminare questo canale.";
"lng_cant_do_this" = "Spiacenti, questa azione non è disponibile.";
"lng_send_button" = "Invia";
@@ -884,11 +985,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_typing" = "sta scrivendo";
"lng_user_typing" = "{user} sta scrivendo";
"lng_users_typing" = "{user} e {second_user} stanno scrivendo";
"lng_many_typing" = "{count:_not_used_|# sta|# stanno} scrivendo";
"lng_many_typing#one" = "{count} sta scrivendo";
"lng_many_typing#other" = "{count} stanno scrivendo";
"lng_playing_game" = "sta giocando a un gioco";
"lng_user_playing_game" = "{user} sta giocando a un gioco";
"lng_users_playing_game" = "{user} e {second_user} stanno giocando a un gioco";
"lng_many_playing_game" = "{count:_not_used_|# sta|# stanno} giocando a un gioco";
"lng_many_playing_game#one" = "{count} sta giocando a un gioco";
"lng_many_playing_game#other" = "{count} stanno giocando a un gioco";
"lng_send_action_record_video" = "sta registrando un video";
"lng_user_action_record_video" = "{user} sta registrando un video";
"lng_send_action_upload_video" = "sta inviando un video";
@@ -909,7 +1012,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_user_action_geo_location" = "{user} sta scegliendo una posizione";
"lng_send_action_choose_contact" = "scegliendo un contatto";
"lng_user_action_choose_contact" = "{user} sta scegliendo un contatto";
"lng_unread_bar" = "{count:_not_used_|# messaggio non letto|# messaggi non letti}";
"lng_unread_bar#one" = "{count} messaggio non letto";
"lng_unread_bar#other" = "{count} messaggi non letti";
"lng_maps_point" = "Posizione";
"lng_save_photo" = "Salva immagine";
@@ -932,7 +1036,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_from_top" = "Togli dall'alto";
"lng_context_promote_admin" = "Rendi amministratore";
"lng_context_remove_admin" = "Rimuovi dagli amministratori";
"lng_context_edit_permissions" = "Modifica autorizzazioni";
"lng_context_restrict_user" = "Limita utente";
"lng_context_remove_from_group" = "Rimuovi dal gruppo";
"lng_context_copy_link" = "Copia link";
@@ -970,31 +1075,41 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_msg" = "Togli messaggio";
"lng_context_cancel_upload" = "Annulla caricamento";
"lng_context_copy_selected" = "Copia testo selezionato";
"lng_context_copy_selected_items" = "Copia selezione come testo";
"lng_context_forward_selected" = "Inoltra selezione";
"lng_context_delete_selected" = "Elimina selezione";
"lng_context_clear_selection" = "Annulla selezione";
"lng_really_send_image" = "Vuoi inviare questa immagine?";
"lng_really_send_file" = "Vuoi inviare questo file?";
"lng_really_share_contact" = "Vuoi condividere questo contatto?";
"lng_send_images_compress" = "Comprimi {count:_not_used_|immagine|immagini}";
"lng_send_images_compress#one" = "Comprimi immagine";
"lng_send_images_compress#other" = "Comprimi immagini";
"lng_send_image_non_local" = "Impossibile inviare un file non locale: {name}";
"lng_send_image_empty" = "Impossibile inviare un file vuoto: {name}";
"lng_send_image_too_large" = "Impossibile inviare il file, perché è più grande di 1500 MB: {name}";
"lng_send_folder" = "Impossibile inviare «{name}» perché è una cartella :(";
"lng_send_images_selected" = "{count:_not_used_|# immagine selezionata|# immagini selezionate}";
"lng_send_photos" = "Invia {count:_not_used_|# foto|# foto}";
"lng_send_files_selected" = "{count:_not_used_|# file selezionato|# file selezionati}";
"lng_send_files" = "Invia {count:_not_used_|# file|# file}";
"lng_send_images_selected#one" = "{count} immagine selezionata";
"lng_send_images_selected#other" = "{count} immagini selezionate";
"lng_send_photos#one" = "Invia {count} foto";
"lng_send_photos#other" = "Invia {count} foto";
"lng_send_files_selected#one" = "{count} file selezionato";
"lng_send_files_selected#other" = "{count} file selezionati";
"lng_send_files#one" = "Invia {count} file";
"lng_send_files#other" = "Invia {count} file";
"lng_forward_choose" = "Scegli destinatario...";
"lng_forward_cant" = "Spiacenti, impossibile inoltrare qui :(";
"lng_forward_confirm" = "Inoltra a {recipient}?";
"lng_forward_share_contact" = "Condividi contatto con {recipient}?";
"lng_forward_share_cant" = "Spiacenti, non è possibile condividere contatti qui :(";
"lng_forward_send_file_confirm" = "Invia «{name}» a {recipient}?";
"lng_forward_send_files_confirm" = "Invia i file selezionati a {recipient}?";
"lng_forward_send_files_cant" = "Spiacenti, non è possibile inviare media qui :(";
"lng_forward_send" = "Invia";
"lng_forward_messages" = "{count:_not_used_|messaggio inoltrato|# messaggi inoltrati}";
"lng_forwarding_from" = "{user} e {count:_not_used_|# altro|altri #}";
"lng_forward_messages#one" = "{count} messaggio inoltrato";
"lng_forward_messages#other" = "{count} messaggi inoltrati";
"lng_forwarding_from#one" = "{user} e {count} altro";
"lng_forwarding_from#other" = "{user} e {count} altri";
"lng_forwarding_from_two" = "{user} e {second_user}";
"lng_inline_switch_choose" = "Scegli conversazione...";
"lng_inline_switch_cant" = "Spiacenti, impossibile scrivere qui :(";
@@ -1016,6 +1131,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_contact_title" = "Modifica nome contatto";
"lng_edit_channel_title" = "Modifica canale";
"lng_edit_sign_messages" = "Firma messaggi";
"lng_edit_group_who_invites" = "Chi può aggiungere membri";
"lng_edit_group_invites_everybody" = "Tutti i membri";
"lng_edit_group_invites_only_admins" = "Solo gli amministratori";
"lng_edit_group" = "Modifica gruppo";
"lng_edit_self_title" = "Modifica il tuo nome";
"lng_confirm_contact_data" = "Nuovo contatto";
@@ -1040,27 +1158,34 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_selected_clear" = "Annulla";
"lng_selected_delete" = "Elimina";
"lng_selected_forward" = "Inoltra";
"lng_selected_count" = "{count:_not_used_|# messaggio|# messaggi}";
"lng_selected_count#one" = "{count} messaggio";
"lng_selected_count#other" = "{count} messaggi";
"lng_selected_cancel_sure_this" = "Annullare il caricamento?";
"lng_selected_upload_stop" = "Arresta ";
"lng_selected_delete_sure_this" = "Vuoi eliminare questo messaggio?";
"lng_selected_delete_sure" = "Vuoi eliminare {count:_not_used_|# messaggio|# messaggi}?";
"lng_selected_delete_sure#one" = "Vuoi eliminare {count} messaggio?";
"lng_selected_delete_sure#other" = "Vuoi eliminare {count} messaggi?";
"lng_delete_photo_sure" = "Vuoi eliminare questa foto?";
"lng_delete_for_everyone_hint" = "Questo {count:_not_used_|lo eliminerà|li eliminerà} per chiunque in questa chat.";
"lng_delete_for_me_chat_hint" = "Questo {count:_not_used_|lo eliminerà|li eliminerà} solo per te, non per gli altri membri della chat.";
"lng_delete_for_me_hint" = "Questo {count:_not_used_|lo eliminerà|li eliminerà} solo per te.";
"lng_delete_for_everyone_hint#one" = "Questo lo eliminerà per chiunque in questa chat.";
"lng_delete_for_everyone_hint#other" = "Questo li eliminerà per chiunque in questa chat.";
"lng_delete_for_me_chat_hint#one" = "Questo lo eliminerà solo per te, non per gli altri membri della chat.";
"lng_delete_for_me_chat_hint#other" = "Questo li eliminerà solo per te, non per gli altri membri della chat.";
"lng_delete_for_me_hint#one" = "Questo lo eliminerà solo per te.";
"lng_delete_for_me_hint#other" = "Questo li eliminerà solo per te.";
"lng_delete_for_everyone_check" = "Elimina per tutti";
"lng_delete_for_other_check" = "Elimina per {user}";
"lng_box_delete" = "Elimina";
"lng_box_leave" = "Lascia";
"lng_about_version" = "versione {version}";
"lng_about_text_1" = "App ufficiale basata sulle [a href=\"https://core.telegram.org/api\"]API di Telegram[/a]\nper velocità e sicurezza.";
"lng_about_text_1" = "App ufficiale basata sulle [a href=\"https://core.telegram.org/api\"]API di Telegram[/a]\nper offrire velocità e sicurezza.";
"lng_about_text_2" = "Questo software è sotto licenza [a href=\"https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\"]GNU GPL[/a] versione 3.\nIl source code è disponibile su [a href=\"https://github.com/telegramdesktop/tdesktop\"]GitHub[/a].";
"lng_about_text_3" = "Visita le {faq_open}domande frequenti{faq_close} per maggiori info.";
"lng_about_done" = "Fatto";
"lng_search_found_results" = "{count:Nessun messaggio trovato|# messaggio trovato|# messaggi trovati}";
"lng_search_no_results" = "Nessun messaggio trovato";
"lng_search_found_results#one" = "Trovato {count} messaggio";
"lng_search_found_results#other" = "Trovati {count} messaggi";
"lng_search_global_results" = "Risultati ricerca globale";
"lng_media_save_progress" = "{ready} di {total} {mb}";
@@ -1169,11 +1294,133 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_player_message_yesterday" = "Ieri alle {time}";
"lng_player_message_date" = "{date} alle {time}";
"lng_rights_edit_admin" = "Modifica amministratore";
"lng_rights_edit_admin_header" = "Cosa può fare questo amministratore?";
"lng_rights_about_add_admins_yes" = "Questo amministratore sarà in grado di aggiungere nuovi amministratori con le stesse autorizzazioni (o più limitate) .";
"lng_rights_about_add_admins_no" = "Questo amministratore non sarà in grado di aggiungere nuovi amministratori.";
"lng_rights_about_admin_cant_edit" = "Non puoi modificare le autorizzazioni di questo amministratore.";
"lng_rights_user_restrictions" = "Restrizioni utente";
"lng_rights_user_restrictions_header" = "Cosa può fare questo utente?";
"lng_rights_channel_info" = "Cambiare le info del canale";
"lng_rights_channel_post" = "Pubblicare messaggi";
"lng_rights_channel_edit" = "Modificare i messaggi di altri";
"lng_rights_channel_delete" = "Eliminare i messaggi di altri";
"lng_rights_group_info" = "Cambiare le info del gruppo";
"lng_rights_group_ban" = "Bloccare utenti";
"lng_rights_group_invite_link" = "Invitare utenti tramite link";
"lng_rights_group_invite" = "Aggiungere utenti";
"lng_rights_group_pin" = "Fissare messaggi";
"lng_rights_group_delete" = "Eliminare messaggi";
"lng_rights_add_admins" = "Aggiungere amministratori";
"lng_rights_chat_read" = "Leggere messaggi";
"lng_rights_chat_send_text" = "Inviare messaggi";
"lng_rights_chat_send_media" = "Inviare media";
"lng_rights_chat_send_stickers" = "Inviare sticker e GIF";
"lng_rights_chat_send_links" = "Inviare link con anteprima";
"lng_rights_chat_banned_until_header" = "Limitato fino a";
"lng_rights_chat_banned_forever" = "Sempre";
"lng_rights_chat_banned_day#one" = "Per {count} giorno";
"lng_rights_chat_banned_day#other" = "Per {count} giorni";
"lng_rights_chat_banned_week#one" = "Per {count} settimana";
"lng_rights_chat_banned_week#other" = "Per {count} settimane";
"lng_rights_chat_banned_custom" = "Personalizzato";
"lng_rights_chat_banned_custom_date" = "Fino a {date}";
"lng_rights_chat_banned_block" = "Blocca e rimuovi dal gruppo";
"lng_restricted_send_message" = "Gli amministratori di questo gruppo ti hanno vietato di scrivere qui.";
"lng_restricted_send_media" = "Spiacenti, gli amministratori di questo gruppo ti hanno vietato di inviare media.";
"lng_restricted_send_stickers" = "Spiacenti, gli amministratori di questo gruppo ti hanno vietato di inviare sticker.";
"lng_restricted_send_gifs" = "Spiacenti, gli amministratori di questo gruppo ti hanno vietato di inviare GIF.";
"lng_restricted_send_inline" = "Gli amministratori di questo gruppo ti hanno vietato di inviare contenuti inline qui.";
"lng_restricted_list_title" = "Utenti limitati";
"lng_banned_list_title" = "Utenti bloccati";
"lng_admin_log_title_all" = "Tutte le azioni";
"lng_admin_log_title_selected" = "Azioni selezionate";
"lng_admin_log_filter" = "Filtro";
"lng_admin_log_filter_title" = "Filtro";
"lng_admin_log_filter_all_actions" = "Tutte le azioni";
"lng_admin_log_filter_restrictions" = "Nuove restrizioni";
"lng_admin_log_filter_admins_new" = "Nuovi amministratori";
"lng_admin_log_filter_members_new" = "Nuovi membri";
"lng_admin_log_filter_info_group" = "Info gruppo";
"lng_admin_log_filter_info_channel" = "Info canale";
"lng_admin_log_filter_messages_deleted" = "Messaggi eliminati";
"lng_admin_log_filter_messages_edited" = "Messaggi modificati";
"lng_admin_log_filter_messages_pinned" = "Messaggi fissati";
"lng_admin_log_filter_members_removed" = "Membri rimossi";
"lng_admin_log_filter_all_admins" = "Tutti gli utenti e amministratori";
"lng_admin_log_about" = "Cos'è questo?";
"lng_admin_log_about_text" = "Questa è una lista di tutte le azioni di servizio eseguite dai membri e dagli amministratori del gruppo nelle ultime 48 ore.";
"lng_admin_log_no_results_title" = "Nessuna azione trovata";
"lng_admin_log_no_results_text" = "Non sono stati trovate azioni recenti che rispondono alla tua richiesta.";
"lng_admin_log_no_results_search_text" = "Non sono state trovate azioni recenti contenenti '{query}'.";
"lng_admin_log_no_events_title" = "Ancora nessuna azione";
"lng_admin_log_no_events_text" = "Non ci sono state azioni di servizio\nda parte dei membri e degli amministratori\ndel gruppo nelle ultime 48 ore.";
"lng_admin_log_empty_text" = "Vuota";
"lng_admin_log_changed_title_group" = "{from} ha cambiato il nome del gruppo in «{title}»";
"lng_admin_log_changed_title_channel" = "{from} ha cambiato il nome del canale in «{title}»";
"lng_admin_log_changed_description_group" = "{from} ha cambiato la descrizione del gruppo:";
"lng_admin_log_removed_description_group" = "{from} ha rimosso la descrizione del gruppo";
"lng_admin_log_changed_description_channel" = "{from} ha cambiato la descrizione del canale:";
"lng_admin_log_removed_description_channel" = "{from} ha rimosso la descrizione del canale";
"lng_admin_log_previous_description" = "Descrizione precedente";
"lng_admin_log_changed_link_group" = "{from} ha cambiato il link del gruppo:";
"lng_admin_log_removed_link_group" = "{from} ha rimosso il link del gruppo";
"lng_admin_log_changed_link_channel" = "{from} ha cambiato il link del canale:";
"lng_admin_log_removed_link_channel" = "{from} ha rimosso il link del canale";
"lng_admin_log_previous_link" = "Link precedente";
"lng_admin_log_changed_photo_group" = "{from} ha cambiato la foto del gruppo";
"lng_admin_log_changed_photo_channel" = "{from} ha cambiato la foto del canale";
"lng_admin_log_removed_photo_group" = "{from} ha rimosso la foto del gruppo";
"lng_admin_log_removed_photo_channel" = "{from} ha rimosso la foto del canale";
"lng_admin_log_invites_enabled" = "{from} ha attivato gli inviti del gruppo";
"lng_admin_log_invites_disabled" = "{from} ha disattivato gli inviti del gruppo";
"lng_admin_log_signatures_enabled" = "{from} ha attivato le firme";
"lng_admin_log_signatures_disabled" = "{from} ha disattivato le firme";
"lng_admin_log_pinned_message" = "{from} ha fissato un messaggio:";
"lng_admin_log_unpinned_message" = "{from} ha tolto un messaggio";
"lng_admin_log_edited_caption" = "{from} ha modificato la didascalia:";
"lng_admin_log_removed_caption" = "{from} ha rimosso la didascalia";
"lng_admin_log_previous_caption" = "Didascalia originale";
"lng_admin_log_edited_message" = "{from} ha modificato un messaggio:";
"lng_admin_log_previous_message" = "Messaggio originale";
"lng_admin_log_deleted_message" = "{from} ha eliminato un messaggio:";
"lng_admin_log_participant_joined" = "{from} si è unito al gruppo";
"lng_admin_log_participant_joined_channel" = "{from} si è unito al canale";
"lng_admin_log_participant_left" = "{from} ha lasciato il gruppo";
"lng_admin_log_participant_left_channel" = "{from} ha lasciato il canale";
"lng_admin_log_invited" = "ha invitato {user}";
"lng_admin_log_banned" = "ha bloccato {user}";
"lng_admin_log_restricted" = "ha cambiato le restrizioni di {user} {until}";
"lng_admin_log_promoted" = "ha cambiato i privilegi di {user}";
"lng_admin_log_user_with_username" = "{name} ({mention})";
"lng_admin_log_restricted_forever" = "indefinitamente";
"lng_admin_log_restricted_until" = "fino a {date}";
"lng_admin_log_banned_view_messages" = "Leggere messaggi";
"lng_admin_log_banned_send_messages" = "Inviare messaggi";
"lng_admin_log_banned_send_media" = "Inviare media";
"lng_admin_log_banned_send_stickers" = "Inviare sticker e GIF";
"lng_admin_log_banned_embed_links" = "Inviare link con anteprima";
"lng_admin_log_admin_change_info" = "Cambiare le info";
"lng_admin_log_admin_post_messages" = "Pubblicare messaggi";
"lng_admin_log_admin_edit_messages" = "Modificare messaggi";
"lng_admin_log_admin_delete_messages" = "Eliminare messaggi";
"lng_admin_log_admin_ban_users" = "Bloccare utenti";
"lng_admin_log_admin_invite_users" = "Aggiungere utenti";
"lng_admin_log_admin_invite_link" = "Invitare utenti tramite link";
"lng_admin_log_admin_pin_messages" = "Fissare messaggi";
"lng_admin_log_admin_add_admins" = "Aggiungere amministratori";
// Not used
"lng_topbar_info" = "Info";
"lng_profile_group_info" = "Info gruppo";
"lng_profile_channel_info" = "Info canale";
"lng_channel_add_admins" = "Nuovo amministratore";
// Wnd specific

View File

@@ -18,8 +18,8 @@ to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
"lng_language_name" = "한국어";
"lng_switch_to_this" = "한국어로 변경";
"lng_language_name" = "어";
"lng_switch_to_this" = "어로 변경";
"lng_menu_contacts" = "주소록";
"lng_menu_calls" = "전화";
@@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_menu_update" = "업데이트";
"lng_menu_restart" = "재시작";
"lng_menu_back" = "뒤로가기";
"lng_menu_night_mode" = "야간 모드";
"lng_disable_notifications_from_tray" = "알림 해제";
"lng_enable_notifications_from_tray" = "알림 사용";
@@ -88,7 +89,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_continue" = "계속";
"lng_close" = "닫기";
"lng_connecting" = "연결중...";
"lng_reconnecting" = "재연결중..{count:now|in # 초|in # 초}...";
"lng_connecting_to_proxy" = "프록시 연결 중...";
"lng_connecting_settings" = "설정";
"lng_reconnecting#one" = "{count} 초 후 재연결...";
"lng_reconnecting#other" = "{count} 초 후 재연결...";
"lng_reconnecting_try_now" = "다시 시도";
"lng_status_service_notifications" = "서비스 알림";
@@ -102,8 +106,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_last_month" = "한 달 이내 마지막으로 접속";
"lng_status_invisible" = "숨김";
"lng_status_lastseen_now" = "방금 접속";
"lng_status_lastseen_minutes" = "{count:_not_used_|#분|#분}전 접속";
"lng_status_lastseen_hours" = "{count:_not_used_|#시간|#시간}전 접속";
"lng_status_lastseen_minutes#one" = "{count} 분전 접속";
"lng_status_lastseen_minutes#other" = "{count} 분전 접속";
"lng_status_lastseen_hours#one" = "{count} 시간전 접속";
"lng_status_lastseen_hours#other" = "{count} 시간전 접속";
"lng_status_lastseen_today" = "오늘 {time}에 마지막으로 접속";
"lng_status_lastseen_yesterday" = "어제 {time}에 마지막으로 접속";
"lng_status_lastseen_date" = "{date}에 마지막으로 접속";
@@ -112,24 +118,43 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_connecting" = "연결중...";
"lng_chat_status_unaccessible" = "그룹에 접근할 수 없습니다.";
"lng_chat_status_members" = "{count:맴버 없음|#명|#명}";
"lng_chat_status_members_online" = "{count:_not_used_|#명|#명}중 {count_online:_not_used_|#명 접속중|#명 접속중}";
"lng_chat_status_no_members" = "대화상대가 없음";
"lng_chat_status_members#one" = "대화상대 {count}";
"lng_chat_status_members#other" = "대화상대 {count}명";
"lng_chat_status_online#one" = "온라인 {count}명";
"lng_chat_status_online#other" = "온라인 {count}명";
"lng_chat_status_members_online" = "{members_count}, {online_count}";
"lng_channel_status" = "채널";
"lng_group_status" = "그룹방";
"lng_channel_members_link" = "{count:_not_used_|#명|#명}";
"lng_channel_admins_link" = "{count:_not_used_|# 관리자|# 관리자}";
"lng_channel_members_link#one" = "대화상대 {count}";
"lng_channel_members_link#other" = "대화상대 {count}명";
"lng_channel_admins_link#one" = "관리자 {count}명";
"lng_channel_admins_link#other" = "관리자 {count}명";
"lng_server_error" = "내부 서버 오류";
"lng_flood_error" = "시도가 너무 많습니다. 나중에 다시 시도해주세요.";
"lng_gif_error" = "GIF 애니메이션을 읽는 동안 에러가 발생하였습니다.";
"lng_gif_error" = "GIF 애니메이션을 불러올때 에러가 발생하였습니다 :(";
"lng_edit_error" = "메시지를 수정 할 수 없습니다.";
"lng_join_channel_error" = "너무 많은 채널과 슈퍼그룹에 참여하였습니다.\n기존 대화방을 나가주셔야 참여가 가능합니다.";
"lng_error_phone_flood" = "죄송합니다, 너무 많이 계정 재가입이 최근에 이루어졌습니다. 다시 재가입까지 몇일 기다려주시기 바랍니다.";
"lng_error_start_minimized_passcoded" = "잠금코드를 설정하였기 때문에 앱이 최소화된 상태에서 시작할 수 없습니다. 앱이 시작하기 전에 잠금코드 입력이 필요합니다.";
"lng_error_pinned_max" = "죄송합니다, {count:_not_used_|# 대화|# 대화} 이상은 맨위 고정할 수 없습니다.";
"lng_error_pinned_max#one" = "죄송합니다, {count}개 이상의 대화를 고정할 수 없습니다.";
"lng_error_pinned_max#other" = "죄송합니다, {count}개 이상의 대화를 고정할 수 없습니다.";
"lng_error_public_groups_denied" = "죄송하지만, 회원님은 공개그룹 참여에 제한 되었습니다.\n{more_info}";
"lng_error_cant_edit_admin" = "죄송합니다, 이 관리자의 권한을 변경 할 수 없습니다.";
"lng_error_cant_add_member" = "죄송합니다, 이 봇을 그룹에 추가할 수 없습니다. 관리자에게 추가 문의해주세요.";
"lng_error_cant_add_bot" = "죄송합니다, 이 봇은 그룹에 추가할 수 없습니다.";
"lng_error_cant_add_admin_invite" = "죄송합니다, 이 사용자는 이 그룹에 속해있지 않으며 초대가 되지 않기 때문에 관리자로 추가할 수 없습니다.";
"lng_error_cant_add_admin_unban" = "죄송합니다, 이 사용자는 블랙리스트에 있으며 해제가 가능하지 않기 떄문에 관리자로 추가할 수 없습니다.";
"lng_error_cant_ban_admin" = "죄송합니다, 이 사용자는 그룹의 관리자이며 해제권한이 없으시기 떄문에 차단할 수 없습니다.";
"lng_sure_add_admin_invite" = "이 사용자는 그룹에 참여하고 있지 않습니다. 그룹에 초대하고 관리자로 추가하시겠습니까?";
"lng_sure_add_admin_unban" = "이 사용자는 제한되어져 있으며 그룹에서 차단당한 상태입니다. 정말로 차단을 해제하고 추가하시겠습니까?";
"lng_sure_ban_admin" = "이 사용자는 그룹의 관리자입니다. 정말로 제한을 하시겠습니까?";
"lng_sure_ban_user_group" = "{user} 를 그룹에서 차단하시겠습니까?";
"lng_sure_enable_socks" = "프록시 설정을 하시겠습니까?\n\n서버: {server}\n포트: {port}\n\n나중에 설정에서 프록시 서버를 변경 할 수 있습니다. (연결유형)";
"lng_sure_enable" = "활성";
"lng_edit_deleted" = "메시지는 삭제 되었습니다.";
"lng_edit_too_long" = "메시지 길이가 너무 깁니다.";
@@ -198,9 +223,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_signin_sure_reset" = "경고!\n\n계정 초기화 진행시 모든 대화,\n메시지 및 공유받은 미디어와 파일이 삭제가 됩니다.\n\n계정 초기화를 진행하시겠습니까?";
"lng_signin_reset" = "초기화";
"lng_signin_reset_wait" = "{phone_number} 계정이 사용중이고 비밀번호 설정이 되어져 있어, 보안을 위하여 1주일 이후에 삭제가 될 예정입니다. 이 설정은 언제든지 취소 할 수 있습니다.\n\n계정은 다음시간 이후에 초기화가 됩니다:\n{when}";
"lng_signin_reset_in_days" = "{count_days:0 일|# 일|# 일} {count_hours:0 시간|# 시간|# 시간} {count_minutes:0 분|# 분|# 분}";
"lng_signin_reset_in_hours" = "{count_hours:0 시간|# 시간|# 시간} {count_minutes:0 분|# 분|# 분}";
"lng_signin_reset_in_minutes" = "{count_minutes:0 분|# 분|# 분}";
"lng_signin_reset_days#one" = "{count} 일";
"lng_signin_reset_days#other" = "{count} 일";
"lng_signin_reset_hours#one" = "{count} 시간";
"lng_signin_reset_hours#other" = "{count} 시간";
"lng_signin_reset_minutes#one" = "{count} 분";
"lng_signin_reset_minutes#other" = "{count} 분";
"lng_signin_reset_in_days" = "{days_count} {hours_count} {minutes_count}";
"lng_signin_reset_in_hours" = "{hours_count} {minutes_count}";
"lng_signin_reset_cancelled" = "현재 사용중인 사용자가 요청하신 계정 초기화를 취소 하였습니다.\n7일 이후에 다시 시도해주세요.";
"lng_signup_title" = "정보";
@@ -223,7 +253,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_settings_save" = "저장";
"lng_settings_upload" = "프로필 이미지 선택";
"lng_settings_crop_profile" = "프로필 사진으로 사용할 사각영역을 선택세요";
"lng_settings_crop_profile" = "프로필 사진으로 설정할 영역을 선택해주세요.";
"lng_settings_uploading_photo" = "사진 업로드 중...";
"lng_settings_edit" = "수정";
"lng_settings_drop_area_subtitle" = "프로필 사진으로 설정";
@@ -241,10 +271,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_username_link" = "회원님과 대화를 할 수 있는 링크입니다.";
"lng_username_copied" = "클립보드에 링크가 복사되었습니다.";
"lng_bio_title" = "자기소개 수정";
"lng_bio_placeholder" = "자기속";
"lng_bio_about" = "소개하고 싶은 내용을 적어주세요. 다른 분이 회원님의 프로필을 확인했을 경우 노출됩니다.";
"lng_settings_section_info" = "정보";
"lng_settings_phone_number" = "전화번호";
"lng_settings_username" = "아이디:";
"lng_settings_choose_username" = "아이디 설정하기";
"lng_settings_empty_bio" = "없음";
"lng_settings_section_notify" = "알림";
"lng_settings_desktop_notify" = "데스크탑 알림";
@@ -303,7 +338,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_backgrounds_header" = "새로운 대화창 배경화면을 선택";
"lng_theme_sure_keep" = "이 테마 색상을 유지하시겠습니까?";
"lng_theme_reverting" = " {count:_not_used_|# 초|# 초} 후에 이전 테마 색상으로 원복합니다.";
"lng_theme_reverting#one" = "{count}초 후에 이전 테마색으로 돌아갑니다.";
"lng_theme_reverting#other" = "{count}초 후에 이전 테마색으로 돌아갑니다.";
"lng_theme_keep_changes" = "변화 유지";
"lng_theme_revert" = "원복";
@@ -329,14 +365,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_local_storage_title" = "로컬 저장소";
"lng_settings_no_data_cached" = "캐시 데이터가 없습니다!";
"lng_settings_images_cached" = "{count:_not_used_|이미지 #개|이미지 #개}, {size}";
"lng_settings_audios_cached" = "{count:_not_used_|음성 메시지 #개|음성 메시지 #개}, {size}";
"lng_settings_images_cached#one" = "이미지 {count}, {size}";
"lng_settings_images_cached#other" = "이미지 {count}, {size}";
"lng_settings_audios_cached#one" = "음성메시지 {count}개, {size}";
"lng_settings_audios_cached#other" = "음성메시지 {count}개, {size}";
"lng_local_storage_clear" = "전체 정리";
"lng_local_storage_clearing" = "초기화 중..";
"lng_local_storage_cleared" = "초기화 완료!";
"lng_local_storage_clear_failed" = "초기화 실패 :(";
"lng_settings_section_advanced_settings" = "고급 설정";
"lng_settings_enable_night_theme" = "야간 모드 활성화";
"lng_settings_disable_night_theme" = "야간 모드 비활성화";
"lng_passcode_remove_button" = "삭제";
@@ -348,8 +388,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_passcode_autolock" = "자동-잠금";
"lng_passcode_autolock_away" = "미사용시 자동잠금 설정";
"lng_passcode_autolock_inactive" = "메신져 미사용시 자동잠금 설정";
"lng_passcode_autolock_minutes" = "{count:_not_used_|#분|#분} ";
"lng_passcode_autolock_hours" = "{count:_not_used_|#시간|#시간} ";
"lng_passcode_autolock_minutes#one" = "{count} ";
"lng_passcode_autolock_minutes#other" = "{count} ";
"lng_passcode_autolock_hours#one" = "{count} 시간";
"lng_passcode_autolock_hours#other" = "{count} 시간";
"lng_passcode_enter_old" = "현재 잠금코드 입력";
"lng_passcode_enter_first" = "잠금코드 입력";
"lng_passcode_enter_new" = "신규 비밀번호 입력";
@@ -455,32 +497,46 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_privacy_lastseen_title" = "접속 시간 개인설정";
"lng_edit_privacy_lastseen_description" = "내 접속시간을 볼 수 있는 사람 설정:";
"lng_edit_privacy_lastseen_warning" = "중요: 접속시간을 공유하지 않는 사람의 접속 시간은 볼 수 없습니다. 대신 대략적으로 접속 시간이 표시 됩니다. (최근에, 일주일 이내, 한달 이내)";
"lng_edit_privacy_lastseen_always" = "항상 공유 {count:| # user| # users}";
"lng_edit_privacy_lastseen_never" = "공유 안함{count:| # user| # users}";
"lng_edit_privacy_lastseen_always_empty" = "항상 공유";
"lng_edit_privacy_lastseen_always#one" = "항상 {count}명의 대화상대와 공유";
"lng_edit_privacy_lastseen_always#other" = "항상 {count}명의 대화상대와 공유";
"lng_edit_privacy_lastseen_never_empty" = "항상 공유 안함";
"lng_edit_privacy_lastseen_never#one" = "항상 {count}명의 대화상대와 공유하지 않음";
"lng_edit_privacy_lastseen_never#other" = "항상 {count}명의 대화상대와 공유하지 않음";
"lng_edit_privacy_lastseen_exceptions" = "이 설정은 위 값을 무효화합니다";
"lng_edit_privacy_lastseen_always_title" = "항상 공유";
"lng_edit_privacy_lastseen_never_title" = "항상 공유 안함";
"lng_edit_privacy_groups_title" = "그룹 초대 설정";
"lng_edit_privacy_groups_description" = "그룹과 채널로 초대가 가능한 사람을 세세하게 설정 할 수 있습니다:";
"lng_edit_privacy_groups_always" = "항상 허용{count:| # user| # users}";
"lng_edit_privacy_groups_never" = "허용 안함{count:| # user| # users}";
"lng_edit_privacy_groups_always_empty" = "항상 허용";
"lng_edit_privacy_groups_always#one" = "항상 {count}명의 대화상대를 허용";
"lng_edit_privacy_groups_always#other" = "항상 {count}명의 대화상대를 허용";
"lng_edit_privacy_groups_never_empty" = "허용 안함";
"lng_edit_privacy_groups_never#one" = "항상 {count}명의 대화상대와 허용하지 않음";
"lng_edit_privacy_groups_never#other" = "항상 {count}명의 대화상대와 허용하지 않음";
"lng_edit_privacy_groups_exceptions" = "위의 설정과 무관하여 이 사용자는 회원님을 그룹이나 채널로 초대할 수 없습니다.";
"lng_edit_privacy_groups_always_title" = "항상 허용";
"lng_edit_privacy_groups_never_title" = "허용 안함";
"lng_edit_privacy_calls_title" = "전화 보안";
"lng_edit_privacy_calls_description" = "전화대상을 제한할 수 있습니다";
"lng_edit_privacy_calls_always" = "항상 허용{count:| # user| # users}";
"lng_edit_privacy_calls_never" = "허용 안함{count:| # user| # users}";
"lng_edit_privacy_calls_always_empty" = "항상 허용";
"lng_edit_privacy_calls_always#one" = "항상 {count}명의 대화상대를 허용";
"lng_edit_privacy_calls_always#other" = "항상 {count}명의 대화상대를 허용";
"lng_edit_privacy_calls_never_empty" = "허용 안함";
"lng_edit_privacy_calls_never#one" = "항상 {count}명의 대화상대와 허용하지 않음";
"lng_edit_privacy_calls_never#other" = "항상 {count}명의 대화상대와 허용하지 않음";
"lng_edit_privacy_calls_exceptions" = "위의 설정과 무관하여 이 사용자는 회원님을 전화를 하거나 할 수 없게 됩니다.";
"lng_edit_privacy_calls_always_title" = "항상 허용";
"lng_edit_privacy_calls_never_title" = "허용 안함";
"lng_self_destruct_title" = "계정 자동탈퇴";
"lng_self_destruct_description" = "이 기간동안 최소한 한번도 온라인이 안될 경우 이 계정은 그룹대화, 일반대화 및 연락처 모두가 삭제됩니다.";
"lng_self_destruct_months" = "{count:_not_used_|# 월|# 월}";
"lng_self_destruct_years" = "{count:_not_used_|# 년|# 년}";
"lng_self_destruct_months#one" = "{count} 월";
"lng_self_destruct_months#other" = "{count} 월";
"lng_self_destruct_years#one" = "{count} 년";
"lng_self_destruct_years#other" = "{count} 년";
"lng_change_phone_title" = "전화번호 변경";
"lng_change_phone_description" = "텔레그램 번호 변경이 가능합니다.\n대화내용, 미디어, 연락처등 클라우드 데이터와\n모든 계정 정보는 새로운 번호로 이관됩니다.\n\n[b]중요[/b]:회원님 텔레그램 연락처에 있는 \n친구분들이회원님의 이전 전화번호가\n저장되어있고 차단을 하지 않았을 경우 \n[b]새로운 전화번호[/b]로 주소록에 저장이 됩니다.";
@@ -506,15 +562,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_invite_link_section" = "초대링크";
"lng_profile_create_public_link" = "공개 링크 생성";
"lng_profile_edit_public_link" = "공개링크 수정";
"lng_profile_search_members" = "사용자 검색";
"lng_profile_manage_admins" = "관리자 관리";
"lng_profile_manage_blocklist" = "차단된 사용자 관리";
"lng_profile_common_groups" = "{count:_not_used_|# 그룹|# 그룹} 공통";
"lng_profile_manage_restrictedlist" = "제한된 사용자 관리";
"lng_profile_recent_actions" = "최근 활동";
"lng_profile_common_groups#one" = "공통그룹 {count}개";
"lng_profile_common_groups#other" = "공통그룹 {count}개";
"lng_profile_common_groups_section" = "공통 그룹";
"lng_profile_participants_section" = "사용자";
"lng_profile_info_section" = "정보";
"lng_profile_mobile_number" = "전화번호:";
"lng_profile_username" = "아이디:";
"lng_profile_link" = "링크:";
"lng_profile_bio" = "자기소개:";
"lng_profile_add_contact" = "연락처 추가";
"lng_profile_edit_contact" = "수정";
"lng_profile_enable_notifications" = "알림";
@@ -536,29 +597,36 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_invite_to_group" = "그룹에 추가";
"lng_profile_delete_contact" = "삭제";
"lng_profile_set_group_photo" = "사진 설정";
"lng_profile_add_participant" = "구성원 추가";
"lng_profile_add_participant" = "대화상대 추가";
"lng_profile_view_channel" = "채널 보기";
"lng_profile_join_channel" = "참여";
"lng_profile_delete_and_exit" = "나가기";
"lng_profile_kick" = "삭제";
"lng_profile_admin" = "관리자";
"lng_profile_edit_permissions" = "수정";
"lng_profile_sure_kick" = "{user}를 내보내시겠습니까?";
"lng_profile_sure_kick_channel" = "{user}를 내보내시겠습니까?";
"lng_profile_sure_kick_admin" = "{user}를 관리자에서 제외 하시겠습니까?";
"lng_profile_loading" = "로드중..";
"lng_profile_shared_media" = "공유된 미디어";
"lng_profile_no_media" = "대화에 미디어가 존재하지 않습니다.";
"lng_profile_photos" = "{count:_not_used_|# 사진|# 사진} ";
"lng_profile_photos#one" = "사진 {count}개";
"lng_profile_photos#other" = "사진 {count}개";
"lng_profile_photos_header" = "사진";
"lng_profile_videos" = "{count:_not_used_|# 동영상|# 동영상}";
"lng_profile_videos#one" = "비디오 {count}";
"lng_profile_videos#other" = "비디오 {count}개";
"lng_profile_videos_header" = "동영상";
"lng_profile_songs" = "{count:_not_used_|# 음성|# 음성}";
"lng_profile_songs#one" = "음성파일 {count}";
"lng_profile_songs#other" = "음성파일 {count}개";
"lng_profile_songs_header" = "음성 파일";
"lng_profile_files" = "{count:_not_used_|# 파일|# 파일}";
"lng_profile_files#one" = "파일 {count}";
"lng_profile_files#other" = "파일 {count}개";
"lng_profile_files_header" = "파일";
"lng_profile_audios" = "{count:_not_used_|# 음성메시지|# 음성메시지}";
"lng_profile_audios#one" = "음성메시지 {count}개";
"lng_profile_audios#other" = "음성메시지 {count}개";
"lng_profile_audios_header" = "음성 메시지";
"lng_profile_shared_links" = "{count:_not_used_|# 공유된 링크|# 공유된 링크}";
"lng_profile_shared_links#one" = "공유링크 {count}";
"lng_profile_shared_links#other" = "공유링크 {count}개";
"lng_profile_shared_links_header" = "공유된 링크";
"lng_profile_copy_phone" = "전화번호 복사";
"lng_profile_copy_fullname" = "이름 복사";
@@ -578,24 +646,29 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_report_button" = "신고";
"lng_report_thanks" = "감사합니다! 신고내용은 신속하게 팀내에서 확인하겠습니다.";
"lng_channel_add_admins" = "새로운 관리자";
"lng_channel_add_members" = "구성원 추가";
"lng_channel_members" = "구성원";
"lng_channel_only_last_shown" = "마지막 {count:_not_used_|# 구성원|# 구성원} 들이 표시가 됩니다.";
"lng_channel_add_members" = "대화상대 추가";
"lng_channel_add_banned" = "사용자 차단";
"lng_channel_add_restricted" = "사용자 제한";
"lng_channel_members" = "대화상대";
"lng_channel_only_last_shown#one" = "마지막 {count}명의 대화상대가 표시됩니다.";
"lng_channel_only_last_shown#other" = "마지막 {count}명의 대화상대가 표시됩니다.";
"lng_channel_admins" = "관리자";
"lng_channel_add_admin" = "관리자 추가";
"lng_channel_admin_sure" = "{user}를 관리자로 추가하시겠습니까?";
"lng_channel_admins_too_much" = "죄송합니다, 관리자의 최대허용치입니다. 다른 관리자를 삭제해주세요";
"lng_channel_admin_status_creator" = "생성자";
"lng_channel_admin_status_promoted_by" = "{user} 님이 승인함";
"lng_channel_admin_status_not_admin" = "관리자가 아님";
"lng_group_blocked_list_about" = "차단된 사용자들은 그룹에서 추방이 되며 관리자의 초대로만 재입장이 가능합니다.\n초대링크는 작동되지 않습니다.";
"lng_chat_all_members_admins" = "모든 구성원이 관리자입니다";
"lng_chat_about_all_admins" = "그룹에 있는 모든 구성원은 상대 초대, 이름 및 사진을 수정할 수 있습니다.";
"lng_chat_all_members_admins" = "모든 대화상대가 관리자입니다";
"lng_chat_about_all_admins" = "그룹에 있는 모든 대화상대는 상대 초대, 이름 및 사진을 수정할 수 있습니다.";
"lng_chat_about_admins" = "그룹에 있는 관리자는 상대 초대, 이름 및 사진을 수정할 수 있습니다.";
"lng_participant_filter" = "검색";
"lng_participant_invite" = "초대";
"lng_participant_invite_sorry" = "죄송합니다, 초기 {count:_not_used|# 구성원|# 구성원} 명만 채널에 개별적으로 추가 할 수 있습니다. \n\n이후로는 초대링크를 통하여 입장해야합니다.";
"lng_participant_invite_sorry#one" = "죄송합니다, 초기 {count}명의 대화상대만 채널에 개별적으로 추가 할 수 있습니다.\n\n이후로는 초대링크를 통하여 입장해야합니다.";
"lng_participant_invite_sorry#other" = "죄송합니다, 초기 {count}명의 대화상대만 채널에 개별적으로 추가 할 수 있습니다.\n\n이후로는 초대링크를 통하여 입장해야합니다.";
"lng_create_group_back" = "뒤로가기";
"lng_create_group_next" = "다음";
"lng_create_group_create" = "만들기";
@@ -618,8 +691,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_create_channel_link_available" = "사용 가능한 링크입니다.";
"lng_create_channel_link_copied" = "링크가 클립보드에 복사되었습니다.";
"lng_create_group_crop" = "그룹프로필 사진으로 사용할 사각영역을 선택하세요";
"lng_create_channel_crop" = "채널프로필 사진으로 사용할 사각영역을 선택하세요";
"lng_create_group_crop" = "그룹프로필 사진으로 사용할 영역을 선택하세요";
"lng_create_channel_crop" = "채널프로필 사진으로 사용할 영역을 선택하세요";
"lng_failed_add_participant" = "사용자를 추가 하지 못하였습니다. 나중에 다시 시도해주세요";
"lng_failed_add_not_mutual" = "죄송합니다. 그룹방에서 대화상대방이 나갔을 경우, 상대 전화번호가 있는 분만 초대가 가능합니다. (서로 전화번호가 등록되어져 있어야만 가능)";
@@ -630,15 +703,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_sure_delete_group_history" = "그룹<<{group}>> 방의 모든 메시지 기록을 삭제하시겠습니까?\n\n이 작업은 취소가 불가능합니다.";
"lng_sure_delete_and_exit" = "그룹 «{group}» 방에서 모든 메시지를 \n삭제하시고 퇴장하시겠습니까?\n\n삭제 하실 경우 취소가 불가능합니다.";
"lng_sure_leave_channel" = "채널에서 나가시겠습니까?";
"lng_sure_delete_channel" = "채널을 삭제하시겠습니까? 모든 구성원과 메시지가 삭제됩니다.";
"lng_sure_delete_channel" = "채널을 삭제하시겠습니까? 모든 대화상대와 메시지가 삭제됩니다.";
"lng_sure_leave_group" = "정말로 그룹방에서 나가시겠습니까?\n이 작업은 취소가 불가능합니다.";
"lng_sure_delete_group" = "정말로 그룹방을 삭제하시겠습니까? 모든 구성원 및 메시지가 삭제됩니다.";
"lng_sure_delete_group" = "정말로 그룹방을 삭제하시겠습니까? 모든 대화상대 및 메시지가 삭제됩니다.";
"lng_message_empty" = "메시지 없음";
"lng_message_unsupported" = "이 메시지는 텔레그램 데스크탑에서 호환이 되지 않습니다. 설정에서 최신 버전으로 업데이트를 하던가 {link}를 통하여 설치해주세요.";
"lng_duration_seconds" = "{count:_not_used_|# 초|# 초}";
"lng_duration_minutes_seconds" = "{count_minutes:# 분|# 분|# 분} {count_seconds:# 초|# 초|# 초}";
"lng_duration_seconds#one" = "{count} 초";
"lng_duration_seconds#other" = "{count} 초";
"lng_duration_minsec_minutes#one" = "{count} 분";
"lng_duration_minsec_minutes#other" = "{count} 분";
"lng_duration_minsec_seconds#one" = "{count} 초";
"lng_duration_minsec_seconds#other" = "{count} 초";
"lng_duration_minutes_seconds" = "{minutes_count} {seconds_count}";
"lng_action_add_user" = "{from} 님께서 {user} 님을 초대하셨습니다.";
"lng_action_add_users_many" = "{from}님께서 {users}명을 초대하였습니다.";
@@ -676,33 +754,49 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_action_pinned_media_sticker" = "스티커";
"lng_action_pinned_media_emoji_sticker" = " {emoji} 스티커";
"lng_action_pinned_media_game" = " «{game}» 게임";
"lng_action_game_score" = "{game} 에서 {from} 님이 {count:#|#|#} 점 획득";
"lng_action_game_you_scored" = "{game} 에서 {count:#|#|#} 점 획득";
"lng_action_game_score_no_game" = "{from} 에서 {count:#|#|#} 점 획득";
"lng_action_game_you_scored_no_game" = "{count:#|#|#} 점 획득";
"lng_action_game_score#one" = "{game} 에서 {from} 님이 {count} 점 획득";
"lng_action_game_score#other" = "{game} 에서 {from} 님이 {count} 점 획득";
"lng_action_game_you_scored#one" = "회원님이 {game}에서 {count}점 획득";
"lng_action_game_you_scored#other" = "회원님이 {game}에서 {count}점 획득";
"lng_action_game_score_no_game#one" = "{from}님이 {count}점 획득";
"lng_action_game_score_no_game#other" = "{from}님이 {count}점 획득";
"lng_action_game_you_scored_no_game#one" = "회원님이 {count}점 획득";
"lng_action_game_you_scored_no_game#other" = "회원님이 {count}점 획득";
"lng_action_payment_done" = "{amount}만큼 {user}에게 성공적으로 전송하였습니다.";
"lng_action_payment_done_for" = "{invoice}에 대하여 {user}에게 {amount}만큼 전송하였습니다.";
"lng_action_took_screenshot" = "{from} 님이 화면을 캡춰했습니다!";
"lng_action_you_took_screenshot" = "화면을 캡춰했습니다!";
"lng_profile_migrate_reached" = "{count:_not_used_|# 명|# 명} 한계치에 도달 되었습니다.";
"lng_ttl_photo_received" = "{from} 님이 자동삭제되는 사진을 전송했습니다. 휴대폰에서 확인해주세요.";
"lng_ttl_photo_sent" = "자동삭제되는 사진을 전송했습니다.";
"lng_ttl_photo_expired" = "사진 유효기간이 만료되었습니다.";
"lng_ttl_video_received" = "{from} 님이 자동삭제되는 동영상을 전송했습니다. 휴대폰에서 확인해주세요.";
"lng_ttl_video_sent" = "자동삭제되는 동영상을 전송했습니다.";
"lng_ttl_video_expired" = "동영상 유효기간이 만료되었습니다.";
"lng_profile_migrate_reached#one" = "대화상대 한계치인 {count}명에 도달했습니다.";
"lng_profile_migrate_reached#other" = "대화상대 한계치인 {count}명에 도달했습니다.";
"lng_profile_migrate_body" = "제한을 초과하고 싶으실 경우, 슈퍼그룹으로 그룹 업그레이드가 가능합니다.";
"lng_profile_migrate_learn_more" = "자세히 »";
"lng_profile_migrate_about" = "최대허용치를 초과하시고 싶으실 경우, 슈퍼그룹방으로 업그레이드해주세요. 슈퍼그룹방은 :";
"lng_profile_migrate_feature1" = "— 최대 {count:_not_used_|# 명|# 명} 까지 허용 가능합니다.";
"lng_profile_migrate_feature2" = "— 모든 구성원이 전체 내용을 볼 수 있습니다.";
"lng_profile_migrate_feature3" = "— 관리자는 구성원으로 부터 메시지 삭제가 가능합니다.";
"lng_profile_migrate_feature1#one" = "— 최대 {count}명까지 허용 가능합니다.";
"lng_profile_migrate_feature1#other" = "— 최대 {count}명까지 허용 가능합니다.";
"lng_profile_migrate_feature2" = "— 모든 대화상대가 전체 내용을 볼 수 있습니다.";
"lng_profile_migrate_feature3" = "— 관리자는 대화상대로 부터 메시지 삭제가 가능합니다.";
"lng_profile_migrate_feature4" = "— 기본값으로 알림이 무음으로 처리됩니다";
"lng_profile_migrate_button" = "슈퍼그룹방으로 업그레이드하기";
"lng_profile_migrate_sure" = "정말로 그룹방을 슈퍼그룹방으로 변환하시겠습니까? 이 작업은 취소가 불가능합니다.";
"lng_profile_convert_button" = "슈퍼그룹으로 전환";
"lng_profile_convert_title" = "슈퍼그룹으로 전환";
"lng_profile_convert_about" = "슈퍼그룹:";
"lng_profile_convert_feature1" = "— 모든 구성원이 이전 대화 내용 조회";
"lng_profile_convert_feature1" = "— 모든 대화상대가 이전 대화 내용 조회";
"lng_profile_convert_feature2" = "— 메시지 삭제시 모두에게 삭제";
"lng_profile_convert_feature3" = "— 관리자는 중요한 메시지 고정 가능";
"lng_profile_convert_feature4" = "— 방 생성자가 그룹 공개링크 생성가능";
"lng_profile_convert_warning" = "{bold_start}주의:{bold_end} 이 작업은 되돌릴 수 없습니다.";
"lng_profile_convert_confirm" = "변환";
"lng_profile_add_more_after_upgrade" = "그룹에서 슈퍼그룹으로 업그레이드시 {count:_not_used_|# 명|# 명} 까지 추가 가능합니다.";
"lng_profile_add_more_after_upgrade#one" = "그룹에서 슈퍼그룹으로 업그레이드시 {count}명까지 추가 가능합니다.";
"lng_profile_add_more_after_upgrade#other" = "그룹에서 슈퍼그룹으로 업그레이드시 {count}명까지 추가 가능합니다.";
"lng_channel_not_accessible" = "채널에 접근 할 수 없습니다.";
"lng_group_not_accessible" = "죄송합니다. 그룹방에 접근 할 수 없습니다.";
@@ -717,7 +811,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_group_invite_want_join_channel" = "«{title}» 채널에 참여하시겠습니까?";
"lng_group_invite_join" = "참여";
"lng_group_invite_members" = "{count:_not_used_|# 회원|# 회원}, 참여자 현황:";
"lng_group_invite_members#one" = "대화상대 {count}, 참여자 현황:";
"lng_group_invite_members#other" = "대화상대 {count}명, 참여자 현황:";
"lng_group_invite_link" = "초대링크: ";
"lng_group_invite_create" = "초대링크 생성";
@@ -730,6 +825,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_channel_public_link_copied" = "클립보드에 링크가 복사되었습니다.";
"lng_forwarded" = "{user}님으로 부터 전달 받음";
"lng_forwarded_date" = "원본: {date}";
"lng_forwarded_channel" = "{channel}로 부터 전달 받음";
"lng_forwarded_via" = "{inline_bot}을 {user}로 부터 전달 받음";
"lng_forwarded_channel_via" = "{inline_bot}을 {channel}로 부터 전달 받음";
@@ -809,7 +905,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_stickers_you_have" = "스티커팩 관리 및 변경";
"lng_stickers_featured" = "인기 스티커";
"lng_stickers_return" = "실행취소";
"lng_stickers_count" = "{count:Loading...|# 스티커|# 스티커}";
"lng_stickers_count#one" = "스티커 {count}";
"lng_stickers_count#other" = "스티커 {count}개";
"lng_stickers_masks_pack" = "마스크 스티커 팩입니다. 모바일 기기에 있는 사진 에디터에서 사용 가능합니다.";
"lng_in_dlg_photo" = "사진";
@@ -839,6 +936,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_cant_invite_banned" = "죄송하지만, 관리자만 회원 추가가 가능합니다.";
"lng_cant_invite_privacy" = "죄송합니다, 개인설정으로 인하여 이 사용자를 그룹에 초대할 수 없습니다.";
"lng_cant_invite_privacy_channel" = "죄송합니다, 개인설정으로 인하여 이 사용자를 채널에 초대할 수 없습니다.";
"lng_cant_delete_group#one" = "죄송합니다, 현재 {count}명 이상 사용자가 있는 그룹일 경우 삭제할 수 없습니다. 삭제하고 싶으실 경우 텔레그램 질문하기로 문의해주세요";
"lng_cant_delete_group#other" = "죄송합니다, 현재 {count}명 이상 사용자가 있는 그룹일 경우 삭제할 수 없습니다. 삭제하고 싶으실 경우 텔레그램 질문하기로 문의해주세요";
"lng_cant_delete_channel#one" = "죄송합니다, 현재 {count}명 이상 사용자가 있는 채널일 경우 삭제할 수 없습니다. 삭제하고 싶으실 경우 텔레그램 질문하기로 문의해주세요.";
"lng_cant_delete_channel#other" = "죄송합니다, 현재 {count}명 이상 사용자가 있는 채널일 경우 삭제할 수 없습니다. 삭제하고 싶으실 경우 텔레그램 질문하기로 문의해주세요.";
"lng_cant_do_this" = "죄송하지만, 할 수 없는 기능입니다.";
"lng_send_button" = "보내기";
@@ -846,8 +947,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_broadcast_ph" = "단체메시지 쓰기...";
"lng_broadcast_silent_ph" = "음소거 메시지...";
"lng_record_cancel" = "이 영역 밖에서 마우스 클릭을 해제하시면 취소가 됩니다.";
"lng_will_be_notified" = "메시지 작성시 구성원들에게 알림이 갑니다.";
"lng_wont_be_notified" = "메시지 작성시 구성원들에게 알림이 가지 않습니다.";
"lng_will_be_notified" = "메시지 작성시 대화상대에게 알림이 갑니다.";
"lng_wont_be_notified" = "메시지 작성시 대화상대에게 알림이 가지 않습니다.";
"lng_willbe_history" = "대화하실 방을 선택해주세요.";
"lng_from_you" = "회원님";
"lng_from_draft" = "임시저장";
@@ -884,11 +985,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_typing" = "입력중";
"lng_user_typing" = "{user}님이 입력중입니다.";
"lng_users_typing" = "{user}님과 {second_user}님이 입력중입니다.";
"lng_many_typing" = "{count:_not_used_|#명이|#명이} 입력중입니다";
"lng_many_typing#one" = "{count}명이 입력중";
"lng_many_typing#other" = "{count}명이 입력중";
"lng_playing_game" = "게임 중";
"lng_user_playing_game" = "{user}님이 게임 중입니다.";
"lng_users_playing_game" = "{user}님과 {second_user}님이 게임 중입니다.";
"lng_many_playing_game" = "{count:_not_used_|#명이|#명이} 게임 중입니다";
"lng_many_playing_game#one" = "{count}명이 게임 중";
"lng_many_playing_game#other" = "{count}명이 게임 중";
"lng_send_action_record_video" = "비디오 녹화 중";
"lng_user_action_record_video" = "{user}님이 녹화중입니다";
"lng_send_action_upload_video" = "비디오 전송 중";
@@ -909,7 +1012,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_user_action_geo_location" = "{user}님이 위치를 선택 중입닏";
"lng_send_action_choose_contact" = "연락처 선택 중";
"lng_user_action_choose_contact" = "{user}님이 연락처를 선택 중입니다";
"lng_unread_bar" = "{count:_not_used_|#개의 읽지 않은 메시지|#개의 읽지 않은 메시지}";
"lng_unread_bar#one" = "안 읽은 메시지 {count}개";
"lng_unread_bar#other" = "안 읽은 메시지 {count}개";
"lng_maps_point" = "위치";
"lng_save_photo" = "사진 저장";
@@ -932,7 +1036,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_from_top" = "상단에서 고정해제";
"lng_context_promote_admin" = "관리자로 지정";
"lng_context_remove_admin" = "관리자에서 제외";
"lng_context_edit_permissions" = "권한 변경";
"lng_context_restrict_user" = "사용자 제한";
"lng_context_remove_from_group" = "그룹에서 추방";
"lng_context_copy_link" = "링크 복사";
@@ -969,32 +1074,42 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_pin_msg" = "메시지 고정";
"lng_context_unpin_msg" = "메시지 고정해제";
"lng_context_cancel_upload" = "업로드 취소";
"lng_context_copy_selected" = "선택한 메시지 복사";
"lng_context_copy_selected" = "선택한 텍스트 복사";
"lng_context_copy_selected_items" = "선택한 내용 텍스트 복사";
"lng_context_forward_selected" = "선택된 메시지 전달";
"lng_context_delete_selected" = "선택된 메시지 삭제";
"lng_context_clear_selection" = "선택된 메시지 초기화";
"lng_really_send_image" = "선택한 이미지를 전송하시겠습니까?";
"lng_really_send_file" = "선택한 파일을 전송하시겠습니까?";
"lng_really_share_contact" = "선택한 연락처를 공유하시겠습니까?";
"lng_send_images_compress" = "압축 {count:_not_used_|이미지|이미지}";
"lng_send_images_compress#one" = "압축된 이미지";
"lng_send_images_compress#other" = "압축된 이미지";
"lng_send_image_non_local" = "로컬이 아닌 파일을 전송 할 수 없습니다 : {name}";
"lng_send_image_empty" = "빈 파일을 전송 할 수 없습니다 : {name}";
"lng_send_image_too_large" = "파일이 1500MB 이상이기 때문에 전송 할 수 없습니다: {name}";
"lng_send_folder" = " «{name}»은 폴더이기 때문에 전송 할 수 없습니다 :(";
"lng_send_images_selected" = "{count:_not_used_|# 이미지|# 이미지} 선택";
"lng_send_photos" = "전송 {count:_not_used_|# 사진|# 사진}";
"lng_send_files_selected" = "{count:_not_used_|# 파일|# 파일} 선택";
"lng_send_files" = "전송 {count:_not_used_|# 파일|# 파일}";
"lng_send_images_selected#one" = "이미지 {count}개가 선택";
"lng_send_images_selected#other" = "이미지 {count}개가 선택됨";
"lng_send_photos#one" = "{count}개의 사진 전송";
"lng_send_photos#other" = "{count}개의 사진 전송";
"lng_send_files_selected#one" = "파일 {count}개 선택됨";
"lng_send_files_selected#other" = "파일 {count}개 선택됨";
"lng_send_files#one" = "{count}개의 파일 전송";
"lng_send_files#other" = "{count}개의 파일 전송";
"lng_forward_choose" = "수신자를 선택..";
"lng_forward_cant" = "이쪽으로 전달 할 수 없습니다 :(";
"lng_forward_confirm" = "{recipient} 님에게 전달하시겠습니까?";
"lng_forward_share_contact" = "{recipient} 님에게 연락처를 공유하시겠습니까?";
"lng_forward_share_cant" = "죄송합니다, 여기서 연락처를 공유할 수 없습니다 :(";
"lng_forward_send_file_confirm" = "«{name}» 을 {recipient} 님에게 보내시겠습니까?";
"lng_forward_send_files_confirm" = "선택한 파일을 {recipient} 님에게 보내시겠습니까?";
"lng_forward_send_files_cant" = "죄송합니다, 여기서 미디어를 공유할 수 없습니다 :(";
"lng_forward_send" = "보내기";
"lng_forward_messages" = "{count:_not_used_|전달받은 메시지|# 개의 전달받은 메시지}";
"lng_forwarding_from" = "{user} 님과 {count:_not_used_|# 명|# 명}";
"lng_forward_messages#one" = "전달된 메시지 {count}개";
"lng_forward_messages#other" = "전달된 메시지 {count}개";
"lng_forwarding_from#one" = "{user}님 이외 {count}명";
"lng_forwarding_from#other" = "{user}님 이외 {count}명";
"lng_forwarding_from_two" = "{user} 님과 {second_user}";
"lng_inline_switch_choose" = "대화 선택...";
"lng_inline_switch_cant" = "죄송합니다. 이쪽으로 글을 쓸 수 없습니다 :(";
@@ -1016,6 +1131,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_contact_title" = "연락처 이름 수정";
"lng_edit_channel_title" = "채널 수정";
"lng_edit_sign_messages" = "서명 메시지";
"lng_edit_group_who_invites" = "초대가 가능한 사용자";
"lng_edit_group_invites_everybody" = "모든 사용자";
"lng_edit_group_invites_only_admins" = "관리자만 가능";
"lng_edit_group" = "그룹방 수정";
"lng_edit_self_title" = "이름 수정";
"lng_confirm_contact_data" = "새로운 연락처";
@@ -1040,15 +1158,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_selected_clear" = "취소";
"lng_selected_delete" = "삭제";
"lng_selected_forward" = "전달";
"lng_selected_count" = "{count:_not_used_|# 메시지|# 메시지}";
"lng_selected_count#one" = "메시지 {count}";
"lng_selected_count#other" = "메시지 {count}개";
"lng_selected_cancel_sure_this" = "업로드를 취소하시겠습니까?";
"lng_selected_upload_stop" = "정지";
"lng_selected_delete_sure_this" = "메시지를 삭제하시겠습니까?";
"lng_selected_delete_sure" = "{count:_not_used_|# 메시지|# 메시지}를 삭제하시겠습니까?";
"lng_selected_delete_sure#one" = "{count}개의 메시지를 삭제하시겠습니까?";
"lng_selected_delete_sure#other" = "{count}개의 메시지를 삭제하시겠습니까?";
"lng_delete_photo_sure" = "사진을 삭제하시겠습니까?";
"lng_delete_for_everyone_hint" = "This will delete {count:_not_used_|it|them} for everyone in this chat.";
"lng_delete_for_me_chat_hint" = "이 작업은 다른분들이 아닌 회원님에게만 {count:_not_used_|이것|이것들} 을 삭제하게됩니다.";
"lng_delete_for_me_hint" = "이 작업은 회원님에게만 {count:_not_used_|이것|이것들} 을 삭제하게됩니다.";
"lng_delete_for_everyone_hint#one" = "이 메시지는 이 대화방에 있는 모두에게 삭제됩니다.";
"lng_delete_for_everyone_hint#other" = "이 메시지는 이 대화방에 있는 모두에게 삭제됩니다.";
"lng_delete_for_me_chat_hint#one" = "이 메시지는 다른 대화상대에게는 삭제가 되지 않고, 회원님에게만 삭제됩니다.";
"lng_delete_for_me_chat_hint#other" = "이 메시지는 다른 대화상대에게는 삭제가 되지 않고, 회원님에게만 삭제됩니다.";
"lng_delete_for_me_hint#one" = "이 메시지는 회원님에게만 삭제됩니다.";
"lng_delete_for_me_hint#other" = "이 메시지는 회원님에게만 삭제됩니다.";
"lng_delete_for_everyone_check" = "모두에게 삭제";
"lng_delete_for_other_check" = "{user}에게 메시지 삭제";
"lng_box_delete" = "삭제";
@@ -1060,7 +1183,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_about_text_3" = "자세한 사항은 {faq_open}Telegram FAQ{faq_close} 에서 확인해주세요.";
"lng_about_done" = "완료";
"lng_search_found_results" = "{count:메시지를 찾을 수 없습니다.|# 개의 메시지를 찾았습니다.|# 개의 메시지를 찾았습니다.}";
"lng_search_no_results" = "메시지를 찾을 수 없습니다.";
"lng_search_found_results#one" = "{count}개의 메시지를 찾았습니다.";
"lng_search_found_results#other" = "{count}개의 메시지를 찾았습니다.";
"lng_search_global_results" = "아이디 검색 결과";
"lng_media_save_progress" = "{ready} / {total} {mb}";
@@ -1169,11 +1294,133 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_player_message_yesterday" = "어제 {time}";
"lng_player_message_date" = "{date} {time}";
"lng_rights_edit_admin" = "관리가 변경";
"lng_rights_edit_admin_header" = "이 관리자가 할 수 있는 것은?";
"lng_rights_about_add_admins_yes" = "이 관리자는 동일한 권한 (혹은 더 제한된) 으로만 관리자 추가가 가능합니다.";
"lng_rights_about_add_admins_no" = "이 관리자는 새로운 관리자 설정을 할 수 없습니다.";
"lng_rights_about_admin_cant_edit" = "이 관리자의 권한을 변경 할 수 없습니다.";
"lng_rights_user_restrictions" = "사용자 제한";
"lng_rights_user_restrictions_header" = "이 사용자가 할 수 있는 것은?";
"lng_rights_channel_info" = "채널 정보 변경";
"lng_rights_channel_post" = "메시지 작성";
"lng_rights_channel_edit" = "다른 사용자 메시지 수정";
"lng_rights_channel_delete" = "다른 사용자 메시지 삭제";
"lng_rights_group_info" = "그룹 정보 변경";
"lng_rights_group_ban" = "사용자 차단";
"lng_rights_group_invite_link" = "링크를 통하여 사용자 초대";
"lng_rights_group_invite" = "사용자 추가";
"lng_rights_group_pin" = "메시지 고정";
"lng_rights_group_delete" = "메시지 삭제";
"lng_rights_add_admins" = "새 관리자 추가";
"lng_rights_chat_read" = "메시지 읽기";
"lng_rights_chat_send_text" = "메시지 전송";
"lng_rights_chat_send_media" = "미디어 전송";
"lng_rights_chat_send_stickers" = "스티커 & GIF 전송";
"lng_rights_chat_send_links" = "링크 저장";
"lng_rights_chat_banned_until_header" = "다음까지 제한";
"lng_rights_chat_banned_forever" = "영원히";
"lng_rights_chat_banned_day#one" = "{count} 일 까지";
"lng_rights_chat_banned_day#other" = "{count} 일 까지";
"lng_rights_chat_banned_week#one" = "{count} 주 까지";
"lng_rights_chat_banned_week#other" = "{count} 주 까지";
"lng_rights_chat_banned_custom" = "커스텀";
"lng_rights_chat_banned_custom_date" = "{date} 일까지";
"lng_rights_chat_banned_block" = "그룹에서 차단 후 추방";
"lng_restricted_send_message" = "그룹 관리자가 회원님의 메시지 작성을 제한했습니다.";
"lng_restricted_send_media" = "그룹 관리자가 회원님의 미디어 전송을 제한했습니다.";
"lng_restricted_send_stickers" = "그룹 관리자가 회원님의 스티커 전송을 제한했습니다.";
"lng_restricted_send_gifs" = "그룹 관리자가 회원님의 GIF 전송을 제한했습니다.";
"lng_restricted_send_inline" = "그룹 관리자가 회원님의 인라인 명령어를 제한했습니다.";
"lng_restricted_list_title" = "제한된 사용자";
"lng_banned_list_title" = "차단된 사용자";
"lng_admin_log_title_all" = "모든 활동";
"lng_admin_log_title_selected" = "선택한 활동";
"lng_admin_log_filter" = "필터";
"lng_admin_log_filter_title" = "필터";
"lng_admin_log_filter_all_actions" = "모든 활동";
"lng_admin_log_filter_restrictions" = "새 제한";
"lng_admin_log_filter_admins_new" = "새 관리자";
"lng_admin_log_filter_members_new" = "새 사용자";
"lng_admin_log_filter_info_group" = "그룹 정보";
"lng_admin_log_filter_info_channel" = "채널 정보";
"lng_admin_log_filter_messages_deleted" = "삭제된 메시지";
"lng_admin_log_filter_messages_edited" = "수정된 메시지";
"lng_admin_log_filter_messages_pinned" = "고정된 메시지";
"lng_admin_log_filter_members_removed" = "추방된 사용자";
"lng_admin_log_filter_all_admins" = "모든 사용자 및 관리자";
"lng_admin_log_about" = "이것은 무엇인가요?";
"lng_admin_log_about_text" = "그룹 사용자와 관리자의 지난 48시간동안 활동 리스트입니다.";
"lng_admin_log_no_results_title" = "활동내역을 찾을 수 없습니다.";
"lng_admin_log_no_results_text" = "검색한 내용의 활동을 찾을 수 없습니다.";
"lng_admin_log_no_results_search_text" = "검색한 '{query}'을/를 포함한 활동을 찾을 수 없습니다.";
"lng_admin_log_no_events_title" = "아직 활동내역이 없습니다.";
"lng_admin_log_no_events_text" = "그룹 사용자와 관리자의\n지난 48시간동안\n활동 리스트입니다.";
"lng_admin_log_empty_text" = "없음";
"lng_admin_log_changed_title_group" = "{from} 님이 그룹이름을 «{title}» 로 바꾸셨습니다.";
"lng_admin_log_changed_title_channel" = "{from} 님이 채널이름을 «{title}» 로 바꾸셨습니다.";
"lng_admin_log_changed_description_group" = "{from} 님이 그룹정보을 변경했습니다:";
"lng_admin_log_removed_description_group" = "{from} 님이 그룹정보을 삭제했습니다.";
"lng_admin_log_changed_description_channel" = "{from} 님이 채널정보을 변경했습니다:";
"lng_admin_log_removed_description_channel" = "{from} 님이 그룹정보을 삭제했습니다.";
"lng_admin_log_previous_description" = "기존 정보";
"lng_admin_log_changed_link_group" = "{from} 님이 그룹링크을 변경했습니다:";
"lng_admin_log_removed_link_group" = "{from} 님이 그룹링크를 삭제했습니다.";
"lng_admin_log_changed_link_channel" = "{from} 님이 채널링크를 변경했습니다:";
"lng_admin_log_removed_link_channel" = "{from} 님이 채널링크를 삭제했습니다.";
"lng_admin_log_previous_link" = "기존 링크";
"lng_admin_log_changed_photo_group" = "{from} 님이 그룹사진을 변경했습니다.";
"lng_admin_log_changed_photo_channel" = "{from} 님이 채널사진을 변경했습니다.";
"lng_admin_log_removed_photo_group" = "{from} 님이 그룹사진을 삭제했습니다.";
"lng_admin_log_removed_photo_channel" = "{from} 님이 채널사진을 삭제했습니다.";
"lng_admin_log_invites_enabled" = "{from} 님이 그룹 초대를 허용했습니다.";
"lng_admin_log_invites_disabled" = "{from} 님이 그룹 초대를 금지했습니다.";
"lng_admin_log_signatures_enabled" = "{from} 님이 서명을 허용했습니다.";
"lng_admin_log_signatures_disabled" = "{from} 님이 서명을 금지했습니다.";
"lng_admin_log_pinned_message" = "{from} 님이 메시지를 고정했습니다:";
"lng_admin_log_unpinned_message" = "{from} 님이 메시지 고정을 해제했습니다.";
"lng_admin_log_edited_caption" = "{from} 님이 자막을 변경했습니다:";
"lng_admin_log_removed_caption" = "{from} 님이 자막은 삭제했습니다";
"lng_admin_log_previous_caption" = "원본 자막";
"lng_admin_log_edited_message" = "{from} 님이 메시지를 수정했습니다:";
"lng_admin_log_previous_message" = "원본 메시지";
"lng_admin_log_deleted_message" = "{from} 님이 메시지를 삭제했습니다:";
"lng_admin_log_participant_joined" = "{from} 님이 그룹에 들어왔습니다.";
"lng_admin_log_participant_joined_channel" = "{from} 님이 채널에 들어오셨습니다.";
"lng_admin_log_participant_left" = "{from} 님이 그룹을 나갔습니다.";
"lng_admin_log_participant_left_channel" = "{from} 님이 채널을 나가셨습니다.";
"lng_admin_log_invited" = "{user} 님을 초대함";
"lng_admin_log_banned" = "{user} 님을 차단함";
"lng_admin_log_restricted" = "{until}까지 {user}에 대한 제한을 변경함";
"lng_admin_log_promoted" = "{user}님에 대한 권한을 변경함";
"lng_admin_log_user_with_username" = "{name} ({mention})";
"lng_admin_log_restricted_forever" = "기한 없음";
"lng_admin_log_restricted_until" = "{date} 까지";
"lng_admin_log_banned_view_messages" = "메시지 읽기";
"lng_admin_log_banned_send_messages" = "메시지 전송";
"lng_admin_log_banned_send_media" = "미디어 전송";
"lng_admin_log_banned_send_stickers" = "스티커 & GIF 전송";
"lng_admin_log_banned_embed_links" = "링크 저장";
"lng_admin_log_admin_change_info" = "채널 정보";
"lng_admin_log_admin_post_messages" = "메시지 작성";
"lng_admin_log_admin_edit_messages" = "메시지 수정";
"lng_admin_log_admin_delete_messages" = "메시지 삭제";
"lng_admin_log_admin_ban_users" = "사용자 차단";
"lng_admin_log_admin_invite_users" = "사용자 추가";
"lng_admin_log_admin_invite_link" = "링크를 통하여 사용자 초대";
"lng_admin_log_admin_pin_messages" = "메시지 고정";
"lng_admin_log_admin_add_admins" = "새 관리자 추가";
// Not used
"lng_topbar_info" = "정보";
"lng_profile_group_info" = "그룹 정보";
"lng_profile_channel_info" = "채널 정보";
"lng_channel_add_admins" = "새로운 관리자";
// Wnd specific

View File

@@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_menu_update" = "Bijwerken";
"lng_menu_restart" = "Herstarten";
"lng_menu_back" = "Vorige";
"lng_menu_night_mode" = "Nachtmodus";
"lng_disable_notifications_from_tray" = "Meldingen uitschakelen";
"lng_enable_notifications_from_tray" = "Meldingen inschakelen";
@@ -88,7 +89,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_continue" = "Doorgaan";
"lng_close" = "Sluiten";
"lng_connecting" = "Verbinden...";
"lng_reconnecting" = "Opnieuw verbinden {count:nu|over # s|over # s}...";
"lng_connecting_to_proxy" = "Verbinden via proxy...";
"lng_connecting_settings" = "Instellingen";
"lng_reconnecting#one" = "Opnieuw verbinden over {count}s...";
"lng_reconnecting#other" = "Opnieuw verbinden over {count}s...";
"lng_reconnecting_try_now" = "Probeer nu";
"lng_status_service_notifications" = "servicemeldingen";
@@ -102,8 +106,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_last_month" = "afgelopen maand gezien";
"lng_status_invisible" = "onzichtbaar";
"lng_status_lastseen_now" = "laatst gezien zojuist";
"lng_status_lastseen_minutes" = "laatst gezien {count:_not_used_|# min|# min} geleden";
"lng_status_lastseen_hours" = "laatst gezien {count:_not_used_|# uur|# uur} geleden";
"lng_status_lastseen_minutes#one" = "laatst gezien {count} minuut geleden";
"lng_status_lastseen_minutes#other" = "laatst gezien {count} minuten geleden";
"lng_status_lastseen_hours#one" = "laatst gezien {count} uur geleden";
"lng_status_lastseen_hours#other" = "laatst gezien {count} uur geleden";
"lng_status_lastseen_today" = "laatst gezien vandaag om {time}";
"lng_status_lastseen_yesterday" = "laatst gezien gisteren om {time}";
"lng_status_lastseen_date" = "laatst gezien {date}";
@@ -112,24 +118,43 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_connecting" = "verbinden...";
"lng_chat_status_unaccessible" = "Groep is ontoegankelijk";
"lng_chat_status_members" = "{count:geen leden|# lid|# leden}";
"lng_chat_status_members_online" = "{count:_not_used_|# lid|# leden}, {count_online:_not_used_|# online|# online}";
"lng_chat_status_no_members" = "geen leden";
"lng_chat_status_members#one" = "{count} lid";
"lng_chat_status_members#other" = "{count} leden";
"lng_chat_status_online#one" = "{count} online";
"lng_chat_status_online#other" = "{count} online";
"lng_chat_status_members_online" = "{members_count}, {online_count}";
"lng_channel_status" = "kanaal";
"lng_group_status" = "groep";
"lng_channel_members_link" = "{count:_not_used_|# lid|# leden}";
"lng_channel_admins_link" = "{count:_not_used_|# beheerder|# beheerders}";
"lng_channel_members_link#one" = "{count} lid";
"lng_channel_members_link#other" = "{count} leden";
"lng_channel_admins_link#one" = "{count} beheerder";
"lng_channel_admins_link#other" = "{count} beheerders";
"lng_server_error" = "Interne serverfout.";
"lng_flood_error" = "Teveel pogingen. Probeer het later opnieuw.";
"lng_gif_error" = "Er is iets een fout opgetreden bij het lezen van de GIF :(";
"lng_gif_error" = "Er is iets misgegaan bij het lezen van deze GIF :(";
"lng_edit_error" = "Je mag dit bericht niet wijzigen";
"lng_join_channel_error" = "Je bent lid van teveel kanalen of supergroepen, verlaat er wat om hier lid te worden.";
"lng_error_phone_flood" = "Je hebt in korte tijd je account veelvuldig verwijderd en opnieuw aangemaakt. Probeer het over een aantal dagen nog eens.";
"lng_error_start_minimized_passcoded" = "Je hebt een toegangscode ingesteld, deze zal ingegeven moeten worden na het starten, geminimaliseerd starten kan daarom niet.";
"lng_error_pinned_max" = "Sorry, je kunt niet meer dan {count:_not_used_|# chat|# chats} vastzetten.";
"lng_error_pinned_max#one" = "Sorry, je kunt niet meer dan {count} chat vastzetten.";
"lng_error_pinned_max#other" = "Sorry, je kunt niet meer dan {count} chats vastzetten.";
"lng_error_public_groups_denied" = "Sorry, je mag niet meer deelnemen aan publieke groepen.\n{more_info}";
"lng_error_cant_edit_admin" = "Je kunt de machtigingen van deze beheerder niet wijzigen.";
"lng_error_cant_add_member" = "Je kunt deze bot niet toevoegen aan de groep, vraag een beheerder om dit te doen.";
"lng_error_cant_add_bot" = "Deze bot kan geen lid worden van groepen.";
"lng_error_cant_add_admin_invite" = "Je mag geen leden toevoegen, deze gebruiker is nog geen lid en kan daarom niet worden toegevoegd als beheerder.";
"lng_error_cant_add_admin_unban" = "Je mag geen leden deblokkeren, deze gebruiker is geblokkeerd en kan daarom niet worden toegevoegd als beheerder.";
"lng_error_cant_ban_admin" = "Je mag geen leden degraderen, deze gebruiker is een beheerder en kan daarom niet worden geblokkeerd.";
"lng_sure_add_admin_invite" = "Deze gebruiker is geen lid, gebruiker toevoegen als beheerder?";
"lng_sure_add_admin_unban" = "Deze gebruiker is geblokkeerd of beperkt, wil je dit ongedaan maken en de gebruiker promoveren?";
"lng_sure_ban_admin" = "Deze gebruiker is een beheerder, gebruiker echt beperken?";
"lng_sure_ban_user_group" = "{user} blokkeren en verwijderen?";
"lng_sure_enable_socks" = "Proxy echt inschakelen? \n\nHost: {server}\nPoort: {port}\n\nJe kunt de proxy-server aanpassen via Instellingen (Verbindingstype).";
"lng_sure_enable" = "Inschakelen";
"lng_edit_deleted" = "Bericht is gewist";
"lng_edit_too_long" = "Je bericht is te lang";
@@ -198,9 +223,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_signin_sure_reset" = "Let op:\n\nAl je chats, berichten en alle andere data gaan verloren als je verder gaat!\n\nEcht je account resetten?";
"lng_signin_reset" = "Reset";
"lng_signin_reset_wait" = "Account {phone_number} is actief en beveiligd met een wachtwoord, het verwijderen stellen we daarom 1 week uit.\nJe kunt dit proces ieder moment annuleren.\n\nJe account kan gereset worden over:\n{when}";
"lng_signin_reset_in_days" = "{count_days:0 dagen|# dag|# dagen} {count_hours:0 uur|# uur|# uur} {count_minutes:0 minuten|# minuut|# minuten}";
"lng_signin_reset_in_hours" = "{count_hours:0 uur|# uur|# uur} {count_minutes:0 minuten|# minuut|# minuten}";
"lng_signin_reset_in_minutes" = "{count_minutes:0 minuten|# minuut|# minuten}";
"lng_signin_reset_days#one" = "{count} dag";
"lng_signin_reset_days#other" = "{count} dagen";
"lng_signin_reset_hours#one" = "{count} uur";
"lng_signin_reset_hours#other" = "{count} uur";
"lng_signin_reset_minutes#one" = "{count} minuut";
"lng_signin_reset_minutes#other" = "{count} minuten";
"lng_signin_reset_in_days" = "{days_count} {hours_count} {minutes_count}";
"lng_signin_reset_in_hours" = "{hours_count} {minutes_count}";
"lng_signin_reset_cancelled" = "De actieve gebruiker heeft de recente poging om dit account te resetten geannuleerd. Probeer het over 7 dagen nog eens.";
"lng_signup_title" = "Jouw Informatie";
@@ -223,7 +253,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_settings_save" = "Opslaan";
"lng_settings_upload" = "Profielfoto instellen";
"lng_settings_crop_profile" = "Kies een vierkant voor je profielfoto";
"lng_settings_crop_profile" = "Kies een gebied voor je profielfoto";
"lng_settings_uploading_photo" = "Foto uploaden...";
"lng_settings_edit" = "Wijzig";
"lng_settings_drop_area_subtitle" = "om als foto in te stellen";
@@ -241,15 +271,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_username_link" = "Deze link opent een chat met jou:";
"lng_username_copied" = "Link gekopieerd naar klembord.";
"lng_bio_title" = "Bio wijzigen";
"lng_bio_placeholder" = "Bio";
"lng_bio_about" = "Je kunt hier wat over jezelf vertellen. Iedereen die je profiel opent kan deze tekst zien.";
"lng_settings_section_info" = "Info";
"lng_settings_phone_number" = "Telefoonnummer:";
"lng_settings_username" = "Gebruikersnaam:";
"lng_settings_choose_username" = "gebruikersnaam kiezen";
"lng_settings_empty_bio" = "Niets";
"lng_settings_section_notify" = "Meldingen";
"lng_settings_desktop_notify" = "Desktopmeldingen";
"lng_settings_show_name" = "Afzender weergeven";
"lng_settings_show_preview" = "Voorvertoning weergeven";
"lng_settings_show_preview" = "Voorbeeld weergeven";
"lng_settings_use_windows" = "Windows-berichtgeving gebruiken";
"lng_settings_use_native_notifications" = "Linux-berichtgeving gebruiken";
"lng_settings_advanced_notifications" = "Plaats en teller voor meldingen";
@@ -303,7 +338,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_backgrounds_header" = "Kies je nieuwe achtergrond";
"lng_theme_sure_keep" = "Dit thema behouden?";
"lng_theme_reverting" = "Ongedaan maken over {count:_not_used_|# seconde|# seconden}.";
"lng_theme_reverting#one" = "Ongedaan maken over {count} seconde.";
"lng_theme_reverting#other" = "Ongedaan maken over {count} seconden.";
"lng_theme_keep_changes" = "Behouden";
"lng_theme_revert" = "Herstellen";
@@ -329,14 +365,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_local_storage_title" = "Lokale opslag";
"lng_settings_no_data_cached" = "Geen cache-data gevonden!";
"lng_settings_images_cached" = "{count:_not_used_|# afbeelding|# afbeeldingen}, {size}";
"lng_settings_audios_cached" = "{count:_not_used_|# spraakbericht|# spraakberichten}, {size}";
"lng_settings_images_cached#one" = "{count} afbeelding, {size}";
"lng_settings_images_cached#other" = "{count} afbeeldingen, {size}";
"lng_settings_audios_cached#one" = "{count} spraakbericht, {size}";
"lng_settings_audios_cached#other" = "{count} spraakberichten, {size}";
"lng_local_storage_clear" = "Alles wissen";
"lng_local_storage_clearing" = "Wissen...";
"lng_local_storage_cleared" = "Gewist!";
"lng_local_storage_clear_failed" = "Wissen mislukt";
"lng_settings_section_advanced_settings" = "Geavanceerde instellingen";
"lng_settings_enable_night_theme" = "Nachtmodus inschakelen";
"lng_settings_disable_night_theme" = "Nachtmodus uitschakelen";
"lng_passcode_remove_button" = "Verwijder";
@@ -348,8 +388,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_passcode_autolock" = "Automatisch vergrendelen";
"lng_passcode_autolock_away" = "Vergrendelen indien langer afwezig dan:";
"lng_passcode_autolock_inactive" = "Vergrendelen indien langer inactief dan:";
"lng_passcode_autolock_minutes" = "{count:_not_used_|# minuut|# minuten}";
"lng_passcode_autolock_hours" = "{count:_not_used_|# uur|# uur}";
"lng_passcode_autolock_minutes#one" = "{count} minuut";
"lng_passcode_autolock_minutes#other" = "{count} minuten";
"lng_passcode_autolock_hours#one" = "{count} uur";
"lng_passcode_autolock_hours#other" = "{count} uur";
"lng_passcode_enter_old" = "Toegangscode invoeren";
"lng_passcode_enter_first" = "Toegangscode invoeren";
"lng_passcode_enter_new" = "Nieuwe toegangscode invoeren";
@@ -402,7 +444,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_connection_try_ipv6" = "Verbinden via IPV6";
"lng_connection_host_ph" = "Hostnaam";
"lng_connection_port_ph" = "Poort";
"lng_connection_user_ph" = "Gebruikersnaam";
"lng_connection_user_ph" = "Gebruiker";
"lng_connection_password_ph" = "Wachtwoord";
"lng_connection_save" = "Opslaan";
@@ -455,32 +497,46 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_privacy_lastseen_title" = "Laatst gezien";
"lng_edit_privacy_lastseen_description" = "Je kunt instellen wie je \"laatst gezien\" mag zien:";
"lng_edit_privacy_lastseen_warning" = "Let op: van mensen waarmee je je laatst gezien tijd niet deelt is deze voor jou ook niet zichtbaar. In plaats daarvan krijg je tijden bij benadering te zien.";
"lng_edit_privacy_lastseen_always" = "Altijd delen met{count:| # gebruiker| # gebruikers}";
"lng_edit_privacy_lastseen_never" = "Nooit delen met{count:| # gebruiker| # gebruikers}";
"lng_edit_privacy_lastseen_always_empty" = "Altijd delen met";
"lng_edit_privacy_lastseen_always#one" = "Altijd delen met {count} gebruiker";
"lng_edit_privacy_lastseen_always#other" = "Altijd delen met {count} gebruikers";
"lng_edit_privacy_lastseen_never_empty" = "Nooit delen met";
"lng_edit_privacy_lastseen_never#one" = "Nooit delen met {count} gebruiker";
"lng_edit_privacy_lastseen_never#other" = "Nooit delen met {count} gebruikers";
"lng_edit_privacy_lastseen_exceptions" = "Deze instelling overschrijft de bovenstaande.";
"lng_edit_privacy_lastseen_always_title" = "Altijd delen met";
"lng_edit_privacy_lastseen_never_title" = "Nooit delen met";
"lng_edit_privacy_groups_title" = "Wie kan mij toevoegen?";
"lng_edit_privacy_groups_description" = "Je kunt nauwkeurig bepalen wie je aan groepen en kanalen mag toevoegen.";
"lng_edit_privacy_groups_always" = "Altijd toestaan{count:| # gebruiker| # gebruikers}";
"lng_edit_privacy_groups_never" = "Nooit toestaan{count:| # gebruiker| # gebruikers}";
"lng_edit_privacy_groups_always_empty" = "Altijd toestaan";
"lng_edit_privacy_groups_always#one" = "Altijd toestaan {count} gebruiker";
"lng_edit_privacy_groups_always#other" = "Altijd toestaan {count} gebruikers";
"lng_edit_privacy_groups_never_empty" = "Nooit toestaan";
"lng_edit_privacy_groups_never#one" = "Nooit toestaan {count} gebruiker";
"lng_edit_privacy_groups_never#other" = "Nooit toestaan {count} gebruikers";
"lng_edit_privacy_groups_exceptions" = "Deze gebruikers altijd toestaan of verbieden je aan groepen en kanalen toe te voegen.";
"lng_edit_privacy_groups_always_title" = "Altijd toestaan";
"lng_edit_privacy_groups_never_title" = "Nooit toestaan";
"lng_edit_privacy_calls_title" = "Oproepen";
"lng_edit_privacy_calls_description" = "Je kunt bepalen wie jou mag bellen:";
"lng_edit_privacy_calls_always" = "Altijd toestaan{count:| # gebruiker| # gebruikers}";
"lng_edit_privacy_calls_never" = "Nooit toestaan{count:| # gebruiker| # gebruikers}";
"lng_edit_privacy_calls_always_empty" = "Altijd toestaan";
"lng_edit_privacy_calls_always#one" = "Altijd toestaan {count} gebruiker";
"lng_edit_privacy_calls_always#other" = "Altijd toestaan {count} gebruikers";
"lng_edit_privacy_calls_never_empty" = "Nooit toestaan";
"lng_edit_privacy_calls_never#one" = "Nooit toestaan {count} gebruiker";
"lng_edit_privacy_calls_never#other" = "Nooit toestaan {count} gebruikers";
"lng_edit_privacy_calls_exceptions" = "Deze gebruikers altijd toestaan of verbieden om je te bellen.";
"lng_edit_privacy_calls_always_title" = "Altijd toestaan";
"lng_edit_privacy_calls_never_title" = "Nooit toestaan";
"lng_self_destruct_title" = "Account verwijderen";
"lng_self_destruct_description" = "Als je binnen deze periode niet minimaal één keer ingelogd bent geweest zal je account worden verwijderd, inclusief alle data.";
"lng_self_destruct_months" = "{count:_not_used_|# maand|# maanden}";
"lng_self_destruct_years" = "{count:_not_used_|# jaar|# jaar}";
"lng_self_destruct_months#one" = "{count} maand";
"lng_self_destruct_months#other" = "{count} maanden";
"lng_self_destruct_years#one" = "{count} jaar";
"lng_self_destruct_years#other" = "{count} jaar";
"lng_change_phone_title" = "Telefoonnummer wijzigen";
"lng_change_phone_description" = "Je kan je telefoonnummer hier wijzigen.\nAl je clouddata — berichten, bestanden, \ngroepen, contacten, etc. zullen worden \ngekopieërd naar je nieuwe nummer.\n\n[b]Let op[/b]: al je Telegram-contacten krijgen \nje [b]nieuwe nummer[/b] in hun adresboek, ervan \nuitgaande dat ze je oude nummer hadden en \nje hen niet had geblokkeerd in Telegram.";
@@ -506,15 +562,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_invite_link_section" = "Uitnodigingslink";
"lng_profile_create_public_link" = "Publieke link maken";
"lng_profile_edit_public_link" = "Publieke link wijzigen";
"lng_profile_search_members" = "Zoek leden";
"lng_profile_manage_admins" = "Beheerders wijzigen";
"lng_profile_manage_blocklist" = "Geblokkeerde gebruikers";
"lng_profile_common_groups" = "gedeelde {count:_not_used_|# groep|# groepen}";
"lng_profile_manage_blocklist" = "Beheer geblokkeerde gebruikers";
"lng_profile_manage_restrictedlist" = "Beheer beperkte gebruikers";
"lng_profile_recent_actions" = "Recente gebeurtenissen";
"lng_profile_common_groups#one" = "{count} gedeelde groep";
"lng_profile_common_groups#other" = "{count} gedeelde groepen";
"lng_profile_common_groups_section" = "Gedeelde groepen";
"lng_profile_participants_section" = "Leden";
"lng_profile_info_section" = "Informatie";
"lng_profile_mobile_number" = "Mobiel:";
"lng_profile_username" = "Gebruikersnaam:";
"lng_profile_link" = "Link:";
"lng_profile_bio" = "Bio:";
"lng_profile_add_contact" = "Contact toevoegen";
"lng_profile_edit_contact" = "Wijzig";
"lng_profile_enable_notifications" = "Meldingen";
@@ -542,23 +603,30 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_delete_and_exit" = "Verlaat";
"lng_profile_kick" = "Verwijder";
"lng_profile_admin" = "beheerder";
"lng_profile_edit_permissions" = "Wijzig";
"lng_profile_sure_kick" = "{user} uit de groep verwijderen?";
"lng_profile_sure_kick_channel" = "{user} uit het kanaal verwijderen?";
"lng_profile_sure_kick_admin" = "{user} ontslaan als beheerder?";
"lng_profile_loading" = "Laden...";
"lng_profile_shared_media" = "Gedeelde media";
"lng_profile_no_media" = "Geen media in deze chat.";
"lng_profile_photos" = "{count:_not_used_|# foto|# foto's}";
"lng_profile_photos#one" = "{count} foto";
"lng_profile_photos#other" = "{count} foto's";
"lng_profile_photos_header" = "Foto's";
"lng_profile_videos" = "{count:_not_used_|# video|# video's}";
"lng_profile_videos#one" = "{count} video";
"lng_profile_videos#other" = "{count} video's";
"lng_profile_videos_header" = "Video's";
"lng_profile_songs" = "{count:_not_used_|# audiobestand|# audiobestanden}";
"lng_profile_songs#one" = "{count} audiobestand";
"lng_profile_songs#other" = "{count} audiobestanden";
"lng_profile_songs_header" = "Audiobestanden";
"lng_profile_files" = "{count:_not_used_|# bestand|# bestanden}";
"lng_profile_files#one" = "{count} bestand";
"lng_profile_files#other" = "{count} bestanden";
"lng_profile_files_header" = "Bestanden";
"lng_profile_audios" = "{count:_not_used_|# spraakbericht|# spraakberichten}";
"lng_profile_audios#one" = "{count} spraakbericht";
"lng_profile_audios#other" = "{count} spraakberichten";
"lng_profile_audios_header" = "Spraakberichten";
"lng_profile_shared_links" = "{count:_not_used_|# gedeelde link|# gedeelde links}";
"lng_profile_shared_links#one" = "{count} gedeelde link";
"lng_profile_shared_links#other" = "{count} gedeelde links";
"lng_profile_shared_links_header" = "Gedeelde links";
"lng_profile_copy_phone" = "Telefoonnummer kopiëren";
"lng_profile_copy_fullname" = "Naam kopiëren";
@@ -578,16 +646,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_report_button" = "Melden";
"lng_report_thanks" = "Bedankt! Je melding zal zo spoedig mogen worden gecontroleerd.";
"lng_channel_add_admins" = "Beheerder toevoegen";
"lng_channel_add_members" = "Leden toevoegen";
"lng_channel_add_banned" = "Blokkeer gebruiker";
"lng_channel_add_restricted" = "Beperk gebruiker";
"lng_channel_members" = "Leden";
"lng_channel_only_last_shown" = "De laatste {count:_not_used_|# lid|# leden} worden hier weergegeven";
"lng_channel_only_last_shown#one" = "Het laatste {count} lid wordt hier weergegeven";
"lng_channel_only_last_shown#other" = "De laatste {count} leden worden hier weergegeven";
"lng_channel_admins" = "Beheerders";
"lng_channel_add_admin" = "Beheerder toevoegen";
"lng_channel_admin_sure" = "{user} aan beheerders toevoegen?";
"lng_channel_admins_too_much" = "Je hebt het maximum aantal beheerders voor deze groep bereikt, Verwijder er eerst één.";
"lng_channel_admin_status_creator" = "Maker";
"lng_channel_admin_status_promoted_by" = "Gepromoveerd door {user}";
"lng_channel_admin_status_not_admin" = "Geen beheerder";
"lng_group_blocked_list_about" = "Geblokkeerde gebruikers worden verwijderd uit de groep, terugkeren kan alleen op uitnodiging van een beheerder.\nUitnodigingslinks werken niet voor hen.";
"lng_group_blocked_list_about" = "Geblokkeerde gebruikers kunnen alleen worden uitgenodigd door beheerders. \nUitnodigingslinks werken niet voor hen.";
"lng_chat_all_members_admins" = "Iedereen is beheerder";
"lng_chat_about_all_admins" = "Iedereen mag leden toevoegen en de groepsfoto of naam wijzigen.";
@@ -595,7 +667,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_participant_filter" = "Zoeken";
"lng_participant_invite" = "Uitnodigen";
"lng_participant_invite_sorry" = "De eerste {count:_not_used|# lid|# leden} kun je persoonlijk uitnodigen.\n\nVanaf nu kunnen mensen lid worden via de uitnodigingslink.";
"lng_participant_invite_sorry#one" = "Het {count}e lid kun je persoonlijk uitnodigen.\n\nVanaf nu kunnen mensen lid worden via de uitnodigingslink.";
"lng_participant_invite_sorry#other" = "De eerste {count} leden kun je persoonlijk uitnodigen.\n\nVanaf nu kunnen mensen lid worden via de uitnodigingslink.";
"lng_create_group_back" = "Vorige";
"lng_create_group_next" = "Volgende";
"lng_create_group_create" = "Maak";
@@ -618,8 +691,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_create_channel_link_available" = "Deze naam is beschikbaar.";
"lng_create_channel_link_copied" = "Link gekopieerd naar klembord";
"lng_create_group_crop" = "Kies een vierkant voor de groepsfoto";
"lng_create_channel_crop" = "Kies een vierkant voor de kanaalfoto";
"lng_create_group_crop" = "Kies een gebied voor de groepsfoto";
"lng_create_channel_crop" = "Kies een gebied voor de kanaalfoto";
"lng_failed_add_participant" = "Gebruiker toevoegen mislukt. Probeer het later.";
"lng_failed_add_not_mutual" = "Iemand die de groep verlaat kan alleen door een wederzijds contact worden toegevoegd (opgeslagen nummers)";
@@ -637,8 +710,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_message_empty" = "Leeg bericht";
"lng_message_unsupported" = "Dit bericht wordt niet ondersteund door jouw versie van Telegram Desktop. Werk bij naar de laatste versie via de instellingen of installeer vanuit {link}";
"lng_duration_seconds" = "{count:_not_used_|# seconde|# seconden}";
"lng_duration_minutes_seconds" = "{count_minutes:# min|# min|# min} {count_seconds:# sec|# sec|# sec}";
"lng_duration_seconds#one" = "{count} seconde";
"lng_duration_seconds#other" = "{count} seconden";
"lng_duration_minsec_minutes#one" = "{count} min";
"lng_duration_minsec_minutes#other" = "{count} min";
"lng_duration_minsec_seconds#one" = "{count} sec";
"lng_duration_minsec_seconds#other" = "{count} sec";
"lng_duration_minutes_seconds" = "{minutes_count} {seconds_count}";
"lng_action_add_user" = "{from} heeft {user} toegevoegd";
"lng_action_add_users_many" = "{from} heeft {users} toegevoegd";
@@ -676,18 +754,33 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_action_pinned_media_sticker" = "een sticker";
"lng_action_pinned_media_emoji_sticker" = "een {emoji} sticker";
"lng_action_pinned_media_game" = "het spel «{game}»";
"lng_action_game_score" = "{from} heeft met {game} {count:# punten|# punt|# punten} behaald";
"lng_action_game_you_scored" = "Je hebt met {game} {count:# punten|# punt|# punten} behaald";
"lng_action_game_score_no_game" = "{from} heeft {count:# punten|# punt|# punten} behaald";
"lng_action_game_you_scored_no_game" = "Je hebt {count:# punten|# punt|# punten} behaald";
"lng_action_game_score#one" = "{from} heeft met {game} {count} punt behaald";
"lng_action_game_score#other" = "{from} heeft met {game} {count} punten behaald";
"lng_action_game_you_scored#one" = "Je hebt met {game} {count} punt behaald";
"lng_action_game_you_scored#other" = "Je hebt met {game} {count} punten behaald";
"lng_action_game_score_no_game#one" = "{from} heeft {count} punt behaald";
"lng_action_game_score_no_game#other" = "{from} heeft {count} punten behaald";
"lng_action_game_you_scored_no_game#one" = "Je hebt {count} punt behaald";
"lng_action_game_you_scored_no_game#other" = "Je hebt {count} punten behaald";
"lng_action_payment_done" = "Je hebt een bedrag van {amount} overgemaakt naar {user}";
"lng_action_payment_done_for" = "Je hebt een bedrag van {amount} overgemaakt naar {user} voor {invoice}";
"lng_action_took_screenshot" = "{from} heeft een schermafdruk gemaakt!";
"lng_action_you_took_screenshot" = "Schermafdruk gemaakt!";
"lng_profile_migrate_reached" = "{count:_not_used_|# lid |# leden} limiet bereikt";
"lng_ttl_photo_received" = "{from} heeft een zelfvernietigende foto gestuurd, bekijk deze op je mobiel.";
"lng_ttl_photo_sent" = "Je hebt een zelfvernietigende foto gestuurd";
"lng_ttl_photo_expired" = "Foto is verlopen";
"lng_ttl_video_received" = "{from} heeft een zelfvernietigende video gestuurd, bekijk deze op je mobiel.";
"lng_ttl_video_sent" = "Je hebt een zelfvernietigende video gestuurd";
"lng_ttl_video_expired" = "Video is verlopen";
"lng_profile_migrate_reached#one" = "{count} ledenlimiet bereikt";
"lng_profile_migrate_reached#other" = "{count} ledenlimiet bereikt";
"lng_profile_migrate_body" = "Wil je een hogere limiet? Waardeer op naar een supergroep.";
"lng_profile_migrate_learn_more" = "Meer informatie »";
"lng_profile_migrate_about" = "Wil je extra functies en een hogere limiet? Waardeer op naar een supergroep:";
"lng_profile_migrate_feature1" = "— Supergroepen hebben tot {count:_not_used_|# lid|# leden}";
"lng_profile_migrate_feature1#one" = "— De ledenlimiet is {count} lid";
"lng_profile_migrate_feature1#other" = "— De ledenlimiet is {count} leden";
"lng_profile_migrate_feature2" = "— Nieuwe leden zien de hele geschiedenis";
"lng_profile_migrate_feature3" = "— Beheerder wist berichten voor iedereen";
"lng_profile_migrate_feature4" = "— Meldingen staan standaard uit";
@@ -702,7 +795,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_convert_feature4" = "— Maker kan een publieke groepslink instellen";
"lng_profile_convert_warning" = "{bold_start}Let op:{bold_end} Je kunt dit niet ongedaan maken.";
"lng_profile_convert_confirm" = "Opwaarderen";
"lng_profile_add_more_after_upgrade" = "Waardeer op naar een supergroep om tot {count:_not_used_|# lid|# leden} toe te voegen";
"lng_profile_add_more_after_upgrade#one" = "Waardeer op naar een supergroep om tot {count} lid toe te voegen";
"lng_profile_add_more_after_upgrade#other" = "Waardeer op naar een supergroep om tot {count} leden toe te voegen";
"lng_channel_not_accessible" = "Sorry, dit kanaal is privé.";
"lng_group_not_accessible" = "Sorry, deze groep is niet beschikbaar.";
@@ -717,19 +811,21 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_group_invite_want_join_channel" = "Wil je lid worden van het kanaal \"{title}\"?";
"lng_group_invite_join" = "Lid worden";
"lng_group_invite_members" = "{count:_not_used_|# lid|# leden}, waaronder:";
"lng_group_invite_members#one" = "{count} lid, waaronder:";
"lng_group_invite_members#other" = "{count} leden, waaronder:";
"lng_group_invite_link" = "Uitnodigingslink:";
"lng_group_invite_create" = "Uitnodigingslink maken";
"lng_group_invite_about" = "Gebruikers kunnen lid worden\nvan je groep via deze link.";
"lng_group_invite_create_new" = "Intrekken";
"lng_group_invite_about_new" = "Je uitnodigingslink zal inactief worden een nieuwe link zal worden gegenereerd.";
"lng_group_invite_create_new" = "Uitnodigingslink intrekken";
"lng_group_invite_about_new" = "Je uitnodigingslink zal inactief worden, een nieuwe link zal worden gegenereerd.";
"lng_group_invite_copied" = "Link gekopieerd naar klembord.";
"lng_group_invite_no_room" = "De ledenlimiet van de groep is bereikt.";
"lng_channel_public_link_copied" = "Link gekopieerd naar klembord";
"lng_forwarded" = "Doorgestuurd van {user}";
"lng_forwarded_date" = "Origineel: {date}";
"lng_forwarded_channel" = "Doorgestuurd van {channel}";
"lng_forwarded_via" = "Doorgestuurd van {user} via {inline_bot}";
"lng_forwarded_channel_via" = "Doorgestuurd van {channel} via {inline_bot}";
@@ -809,7 +905,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_stickers_you_have" = "Beheer en sorteer stickerbundels";
"lng_stickers_featured" = "Populaire stickers";
"lng_stickers_return" = "Ongedaan maken";
"lng_stickers_count" = "{count:Laden..|# sticker|# stickers}";
"lng_stickers_count#one" = "{count} sticker";
"lng_stickers_count#other" = "{count} stickers";
"lng_stickers_masks_pack" = "Dit een set met maskers. Je kunt ze gebruiken als je een foto bewerkt in onze mobiele apps.";
"lng_in_dlg_photo" = "Foto";
@@ -822,7 +919,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_in_dlg_sticker" = "Sticker";
"lng_in_dlg_sticker_emoji" = "{emoji} Sticker";
"lng_ban_user" = "Blacklist gebruiker";
"lng_ban_user" = "Blokkeer gebruiker";
"lng_delete_all_from" = "Verwijder alles van gebruiker";
"lng_report_spam" = "Spam melden";
"lng_report_spam_hide" = "Verbergen";
@@ -839,6 +936,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_cant_invite_banned" = "Alleen beheerders kunnen deze gebruiker toevoegen.";
"lng_cant_invite_privacy" = "Je kunt deze gebruiker niet toevoegen aan groepen door zijn/haar privacyinstellingen.";
"lng_cant_invite_privacy_channel" = "Je kunt deze gebruiker niet toevoegen aan kanalen door zijn/haar privacyinstellingen.";
"lng_cant_delete_group#one" = "Het is momenteel niet mogelijk om via de app groepen te verwijderen met meer dan {count} lid. Neem contact op met de ondersteuning van Telegram om deze groep te verwijderen.";
"lng_cant_delete_group#other" = "Het is momenteel niet mogelijk om via de app groepen te verwijderen met meer dan {count} leden. Neem contact op met de ondersteuning van Telegram om deze groep te verwijderen.";
"lng_cant_delete_channel#one" = "Het is momenteel niet mogelijk om via de app kanalen te verwijderen met meer dan {count} lid. Neem contact op met de ondersteuning van Telegram om dit kanaal te verwijderen.";
"lng_cant_delete_channel#other" = "Het is momenteel niet mogelijk om via de app kanalen te verwijderen met meer dan {count} leden. Neem contact op met de ondersteuning van Telegram om dit kanaal te verwijderen.";
"lng_cant_do_this" = "Deze actie is niet beschikbaar.";
"lng_send_button" = "Stuur";
@@ -884,11 +985,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_typing" = "aan het typen";
"lng_user_typing" = "{user} is aan het typen";
"lng_users_typing" = "{user} en {second_user} zijn aan het typen";
"lng_many_typing" = "{count:_not_used_|# is|# zijn} aan het typen";
"lng_many_typing#one" = "{count} is aan het typen";
"lng_many_typing#other" = "{count} zijn aan het typen";
"lng_playing_game" = "speelt een spel";
"lng_user_playing_game" = "{user} speelt een spel";
"lng_users_playing_game" = "{user} en {second_user} spelen een spel";
"lng_many_playing_game" = "{count:_not_used_|# is|# zijn} een spel aan het spelen";
"lng_many_playing_game#one" = "{count} speelt een spel";
"lng_many_playing_game#other" = "{count} spelen een spel";
"lng_send_action_record_video" = "video opnemen";
"lng_user_action_record_video" = "{user} neemt video op";
"lng_send_action_upload_video" = "video versturen";
@@ -909,7 +1012,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_user_action_geo_location" = "{user} kiest een locatie";
"lng_send_action_choose_contact" = "contact kiezen";
"lng_user_action_choose_contact" = "{user} kiest een contact";
"lng_unread_bar" = "{count:_not_used_|# ongelezen bericht|# ongelezen berichten}";
"lng_unread_bar#one" = "{count} ongelezen bericht";
"lng_unread_bar#other" = "{count} ongelezen berichten";
"lng_maps_point" = "Locatie";
"lng_save_photo" = "Afbeelding opslaan";
@@ -932,7 +1036,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_from_top" = "Losmaken";
"lng_context_promote_admin" = "Beheerder maken";
"lng_context_remove_admin" = "Ontslaan als beheerder";
"lng_context_edit_permissions" = "Machtigingen aanpassen";
"lng_context_restrict_user" = "Beperk gebruiker";
"lng_context_remove_from_group" = "Uit de groep verwijderen";
"lng_context_copy_link" = "Link kopiëren";
@@ -970,31 +1075,41 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_msg" = "Bericht losmaken";
"lng_context_cancel_upload" = "Upload annuleren";
"lng_context_copy_selected" = "Tekstselectie kopiëren";
"lng_context_copy_selected_items" = "Selectie als tekst kopiëren";
"lng_context_forward_selected" = "Selectie doorsturen";
"lng_context_delete_selected" = "Selectie verwijderen";
"lng_context_clear_selection" = "Selectie wissen";
"lng_really_send_image" = "Wil je deze afbeeldingen versturen?";
"lng_really_send_file" = "Wil je dit bestand versturen?";
"lng_really_share_contact" = "Wil je dit contact delen?";
"lng_send_images_compress" = "{count:_not_used_|afbeelding|afbeeldingen} comprimeren";
"lng_send_images_compress#one" = "Afbeelding comprimeren";
"lng_send_images_compress#other" = "Afbeeldingen comprimeren";
"lng_send_image_non_local" = "Verzenden mislukt, {name} is een netwerk-bestand.";
"lng_send_image_empty" = "Verzenden mislukt, {name} is leeg.";
"lng_send_image_too_large" = "Verzenden mislukt, {name} is groter dan 1500 MB.";
"lng_send_folder" = "Kan de map «{name}» niet versturen, kies een bestand. :(";
"lng_send_images_selected" = "{count:_not_used_|# afbeelding|# afbeeldingen} gekozen";
"lng_send_photos" = "{count:_not_used_|# foto|# foto's} versturen";
"lng_send_files_selected" = "{count:_not_used_|# bestand|# bestanden} gekozen";
"lng_send_files" = "{count:_not_used_|# bestand|# bestanden} versturen";
"lng_send_images_selected#one" = "{count} afbeelding gekozen";
"lng_send_images_selected#other" = "{count} afbeeldingen gekozen";
"lng_send_photos#one" = "Stuur {count} foto";
"lng_send_photos#other" = "Stuur {count} foto's";
"lng_send_files_selected#one" = "{count} bestand gekozen";
"lng_send_files_selected#other" = "{count} bestanden gekozen";
"lng_send_files#one" = "Stuur {count} bestand";
"lng_send_files#other" = "Stuur {count} bestanden";
"lng_forward_choose" = "Ontvanger kiezen...";
"lng_forward_cant" = "Sorry, doorsturen hierheen kan niet :(";
"lng_forward_confirm" = "Doorsturen naar {recipient}?";
"lng_forward_share_contact" = "Contact delen met {recipient}?";
"lng_forward_share_cant" = "Sorry, een contact hier delen kan niet :(";
"lng_forward_send_file_confirm" = " \"{name}\" naar {recipient} versturen?";
"lng_forward_send_files_confirm" = "Gekozen bestanden naar {recipient} sturen?";
"lng_forward_send_files_cant" = "Sorry, media sturen hierheen kan niet :(";
"lng_forward_send" = "Stuur";
"lng_forward_messages" = "{count:_not_used_|Doorgestuurd bericht|# doorgestuurde berichten}";
"lng_forwarding_from" = "{user} en {count:_not_used_|# andere|# anderen}";
"lng_forward_messages#one" = "bijlage: {count} bericht";
"lng_forward_messages#other" = "bijlage: {count} berichten";
"lng_forwarding_from#one" = "{user} en {count} andere";
"lng_forwarding_from#other" = "{user} en {count} anderen";
"lng_forwarding_from_two" = "{user} en {second_user}";
"lng_inline_switch_choose" = "Kies chat...";
"lng_inline_switch_cant" = "Sorry, je kunt hier niets delen :(";
@@ -1016,6 +1131,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_contact_title" = "Naam wijzigen";
"lng_edit_channel_title" = "Kanaal wijzigen";
"lng_edit_sign_messages" = "Ondertekenen";
"lng_edit_group_who_invites" = "Wie mag leden toevoegen";
"lng_edit_group_invites_everybody" = "Alle leden";
"lng_edit_group_invites_only_admins" = "Beheerders";
"lng_edit_group" = "Groep wijzigen";
"lng_edit_self_title" = "Je naam wijzigen";
"lng_confirm_contact_data" = "Nieuw contact";
@@ -1040,15 +1158,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_selected_clear" = "Annuleren";
"lng_selected_delete" = "Verwijder";
"lng_selected_forward" = "Doorsturen";
"lng_selected_count" = "{count:_not_used_|# bericht|# berichten}";
"lng_selected_count#one" = "{count} bericht";
"lng_selected_count#other" = "{count} berichten";
"lng_selected_cancel_sure_this" = "Upload annuleren?";
"lng_selected_upload_stop" = "Stoppen";
"lng_selected_delete_sure_this" = "Wil je dit bericht verwijderen?";
"lng_selected_delete_sure" = "Wil je {count:_not_used_|# bericht|# berichten} verwijderen?";
"lng_selected_delete_sure#one" = "{count} bericht verwijderen?";
"lng_selected_delete_sure#other" = "{count} berichten verwijderen?";
"lng_delete_photo_sure" = "Wil je deze foto verwijderen?";
"lng_delete_for_everyone_hint" = "Hiermee verwijder je {count:_not_used_|dit|deze} voor iedereen in de chat.";
"lng_delete_for_me_chat_hint" = "Hiermee verwijder je {count:_not_used_|dit|deze} alleen voor jezelf, niet voor andere leden.";
"lng_delete_for_me_hint" = "Hiermee verwijder je {count:_not_used_|dit|deze} alleen voor jezelf.";
"lng_delete_for_everyone_hint#one" = "Hiermee verwijder je dit voor iedereen in de chat.";
"lng_delete_for_everyone_hint#other" = "Hiermee verwijder je deze voor iedereen in de chat.";
"lng_delete_for_me_chat_hint#one" = "Hiermee verwijder je dit alleen voor jezelf, niet voor andere leden.";
"lng_delete_for_me_chat_hint#other" = "Hiermee verwijder je deze alleen voor jezelf, niet voor andere leden.";
"lng_delete_for_me_hint#one" = "Hiermee verwijder je dit alleen voor jezelf.";
"lng_delete_for_me_hint#other" = "Hiermee verwijder je deze alleen voor jezelf.";
"lng_delete_for_everyone_check" = "Verwijder voor iedereen";
"lng_delete_for_other_check" = "Verwijder voor {user}";
"lng_box_delete" = "Verwijder";
@@ -1060,7 +1183,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_about_text_3" = "Bezoek de {faq_open}Telegram FAQ{faq_close} voor meer informatie.";
"lng_about_done" = "Gereed";
"lng_search_found_results" = "{count:geen berichten gevonden|# berichten gevonden|# berichten gevonden}";
"lng_search_no_results" = "Geen berichten gevonden";
"lng_search_found_results#one" = "{count} berichten gevonden";
"lng_search_found_results#other" = "{count} berichten gevonden";
"lng_search_global_results" = "Wereldwijde zoekresultaten";
"lng_media_save_progress" = "{ready} van {total} {mb}";
@@ -1083,8 +1208,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_mediaview_saved" = "Het bestand is opgeslagen in je [c]Downloads[/c]-map";
"lng_theme_preview_title" = "Thema-voorvertoning";
"lng_theme_preview_generating" = "Voorvertoning van thema maken...";
"lng_theme_preview_title" = "Thema-voorbeeld";
"lng_theme_preview_generating" = "Voorbeeld van thema maken...";
"lng_theme_preview_invalid" = "Thema bevat ongeldige data.";
"lng_theme_preview_apply" = "Thema toepassen";
@@ -1169,11 +1294,133 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_player_message_yesterday" = "Gisteren om {time}";
"lng_player_message_date" = "{date} om {time}";
"lng_rights_edit_admin" = "Beheerder wijzigen";
"lng_rights_edit_admin_header" = "Wat mag deze beheerder?";
"lng_rights_about_add_admins_yes" = "Deze beheerder mag nieuwe beheerders toevoegen met gelijkwaardige machtigingen (of minder) als zichzelf.";
"lng_rights_about_add_admins_no" = "Deze beheerder mag geen beheerders toevoegen";
"lng_rights_about_admin_cant_edit" = "Je kunt de machtigingen van deze beheerder niet wijzigen.";
"lng_rights_user_restrictions" = "Gebruikersbeperkingen";
"lng_rights_user_restrictions_header" = "Wat mag deze gebruiker";
"lng_rights_channel_info" = "Informatie aanpassen";
"lng_rights_channel_post" = "Berichten plaatsen";
"lng_rights_channel_edit" = "Berichten van anderen wijzigen";
"lng_rights_channel_delete" = "Berichten van anderen verwijderen";
"lng_rights_group_info" = "Informatie aanpassen";
"lng_rights_group_ban" = "Leden blokkeren";
"lng_rights_group_invite_link" = "Uitnodigen via link";
"lng_rights_group_invite" = "Leden toevoegen";
"lng_rights_group_pin" = "Berichten vastzetten";
"lng_rights_group_delete" = "Berichten verwijderen";
"lng_rights_add_admins" = "Beheerders toevoegen";
"lng_rights_chat_read" = "Berichten lezen";
"lng_rights_chat_send_text" = "Berichten sturen";
"lng_rights_chat_send_media" = "Media sturen";
"lng_rights_chat_send_stickers" = "GIF's en stickers sturen";
"lng_rights_chat_send_links" = "Link-voorbeeld sturen";
"lng_rights_chat_banned_until_header" = "Beperkt tot";
"lng_rights_chat_banned_forever" = "Voor altijd";
"lng_rights_chat_banned_day#one" = "{count} dag";
"lng_rights_chat_banned_day#other" = "{count} dagen";
"lng_rights_chat_banned_week#one" = "{count} week";
"lng_rights_chat_banned_week#other" = "{count} weken";
"lng_rights_chat_banned_custom" = "Aangepast";
"lng_rights_chat_banned_custom_date" = "Tot {date}";
"lng_rights_chat_banned_block" = "Blokkeer en verwijder uit groep";
"lng_restricted_send_message" = "Je bent beperkt in het plaatsen van berichten door de beheerders.";
"lng_restricted_send_media" = "Je bent beperkt in het sturen van media door de beheerders.";
"lng_restricted_send_stickers" = "Je bent beperkt in het sturen van stickers door de beheerders.";
"lng_restricted_send_gifs" = "Je bent beperkt in het sturen van GIF's door de beheerders.";
"lng_restricted_send_inline" = "Je bent beperkt in het sturen van inline-content door de beheerders.";
"lng_restricted_list_title" = "Beperkte gebruikers";
"lng_banned_list_title" = "Geblokkeerde gebruikers";
"lng_admin_log_title_all" = "Alle gebeurtenissen";
"lng_admin_log_title_selected" = "Geselecteerde gebeurtenissen";
"lng_admin_log_filter" = "Filteren";
"lng_admin_log_filter_title" = "Filteren";
"lng_admin_log_filter_all_actions" = "Alle gebeurtenissen";
"lng_admin_log_filter_restrictions" = "Nieuwe beperkingen";
"lng_admin_log_filter_admins_new" = "Nieuwe beheerders";
"lng_admin_log_filter_members_new" = "Nieuwe leden";
"lng_admin_log_filter_info_group" = "Groepsinformatie";
"lng_admin_log_filter_info_channel" = "Kanaalinformatie";
"lng_admin_log_filter_messages_deleted" = "Verwijderd berichten";
"lng_admin_log_filter_messages_edited" = "Gewijzigde berichten";
"lng_admin_log_filter_messages_pinned" = "Vastgezette berichten";
"lng_admin_log_filter_members_removed" = "Vertrokken leden";
"lng_admin_log_filter_all_admins" = "Alle gebruikers";
"lng_admin_log_about" = "Wat is dit?";
"lng_admin_log_about_text" = "Dit is een lijst met alle acties van de groepsleden en beheerders in de afgelopen 48 uur.";
"lng_admin_log_no_results_title" = "Geen gebeurtenissen";
"lng_admin_log_no_results_text" = "Geen recente gebeurtenissen gevonden voor je zoekopdracht.";
"lng_admin_log_no_results_search_text" = "Geen recente gebeurtenissen voor '{query}' gevonden.";
"lng_admin_log_no_events_title" = "Nog geen gebeurtenissen";
"lng_admin_log_no_events_text" = "Beheerders en leden hebben\ngeen acties uitgevoerd in \nde afgelopen 48 uur.";
"lng_admin_log_empty_text" = "Leeg";
"lng_admin_log_changed_title_group" = "{from} heeft de groepsnaam gewijzigd naar «{title}»";
"lng_admin_log_changed_title_channel" = "{from} heeft de kanaalnaam gewijzigd naar «{title}»";
"lng_admin_log_changed_description_group" = "beschrijving door {from} gewijzigd:";
"lng_admin_log_removed_description_group" = "beschrijving door {from} verwijderd";
"lng_admin_log_changed_description_channel" = "beschrijving door {from} gewijzigd:";
"lng_admin_log_removed_description_channel" = "beschrijving door {from} verwijderd";
"lng_admin_log_previous_description" = "Vorige beschrijving";
"lng_admin_log_changed_link_group" = "link door {from} gewijzigd:";
"lng_admin_log_removed_link_group" = "link door {from} verwijderd:";
"lng_admin_log_changed_link_channel" = "link door {from} gewijzigd:";
"lng_admin_log_removed_link_channel" = "link door {from} verwijderd:";
"lng_admin_log_previous_link" = "Vorige link";
"lng_admin_log_changed_photo_group" = "Groepsafbeelding door {from} gewijzigd";
"lng_admin_log_changed_photo_channel" = "Kanaalfoto door {from} gewijzigd";
"lng_admin_log_removed_photo_group" = "{from} heeft de groepsfoto verwijderd";
"lng_admin_log_removed_photo_channel" = "{from} heeft de kanaalfoto verwijderd";
"lng_admin_log_invites_enabled" = "uitnodigingen door {from} ingeschakeld";
"lng_admin_log_invites_disabled" = "uitnodigingen door {from} uitgeschakeld";
"lng_admin_log_signatures_enabled" = "ondertekenen door {from} ingeschakeld";
"lng_admin_log_signatures_disabled" = "ondertekenen door {from} uitgeschakeld";
"lng_admin_log_pinned_message" = "Bericht door {from} vastgezet:";
"lng_admin_log_unpinned_message" = "Bericht door {from} losgemaakt";
"lng_admin_log_edited_caption" = "Onderschrift door {from} gewijzigd:";
"lng_admin_log_removed_caption" = "Onderschrift door {from} verwijderd";
"lng_admin_log_previous_caption" = "Origineel onderschrift";
"lng_admin_log_edited_message" = "Bericht door {from} gewijzigd";
"lng_admin_log_previous_message" = "Origineel bericht";
"lng_admin_log_deleted_message" = "Bericht door {from} verwijderd";
"lng_admin_log_participant_joined" = "{from} is nu lid van de groep";
"lng_admin_log_participant_joined_channel" = "{from} is nu lid van het kanaal";
"lng_admin_log_participant_left" = "{from} heeft de groep verlaten";
"lng_admin_log_participant_left_channel" = "{from} heeft het kanaal verlaten";
"lng_admin_log_invited" = "{user} uitgenodigd";
"lng_admin_log_banned" = "{user} geblokkeerd";
"lng_admin_log_restricted" = "beperkingen van {user} aangepast tot {until}";
"lng_admin_log_promoted" = "privileges van {user} aangepast";
"lng_admin_log_user_with_username" = "{name} ({mention})";
"lng_admin_log_restricted_forever" = "Voor altijd";
"lng_admin_log_restricted_until" = "tot {date}";
"lng_admin_log_banned_view_messages" = "Berichten lezen";
"lng_admin_log_banned_send_messages" = "Berichten sturen";
"lng_admin_log_banned_send_media" = "Media sturen";
"lng_admin_log_banned_send_stickers" = "Stickers & GIF's sturen";
"lng_admin_log_banned_embed_links" = "Link-voorbeeld sturen";
"lng_admin_log_admin_change_info" = "Informatie aanpassen";
"lng_admin_log_admin_post_messages" = "Berichten plaatsen";
"lng_admin_log_admin_edit_messages" = "Berichten wijzigen";
"lng_admin_log_admin_delete_messages" = "Berichten verwijderen";
"lng_admin_log_admin_ban_users" = "Leden blokkeren";
"lng_admin_log_admin_invite_users" = "Leden toevoegen";
"lng_admin_log_admin_invite_link" = "Uitnodigen via link";
"lng_admin_log_admin_pin_messages" = "Berichten vastzetten";
"lng_admin_log_admin_add_admins" = "Beheerders toevoegen";
// Not used
"lng_topbar_info" = "Info";
"lng_profile_group_info" = "Groepsinformatie";
"lng_profile_channel_info" = "Kanaalinformatie";
"lng_channel_add_admins" = "Beheerder toevoegen";
// Wnd specific

View File

@@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_menu_update" = "Atualizar";
"lng_menu_restart" = "Reiniciar";
"lng_menu_back" = "Voltar";
"lng_menu_night_mode" = "Modo noturno";
"lng_disable_notifications_from_tray" = "Desabilitar notificações";
"lng_enable_notifications_from_tray" = "Habilitar notificações";
@@ -88,7 +89,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_continue" = "Continuar";
"lng_close" = "Fechar";
"lng_connecting" = "Conectando...";
"lng_reconnecting" = "Reconectar {count:agora|em # s|em # s}...";
"lng_connecting_to_proxy" = "Conectando ao proxy...";
"lng_connecting_settings" = "Configurações";
"lng_reconnecting#one" = "Reconectar em {count} s...";
"lng_reconnecting#other" = "Reconectar em {count} s...";
"lng_reconnecting_try_now" = "Tentar agora";
"lng_status_service_notifications" = "notificações de serviço";
@@ -102,8 +106,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_last_month" = "visto há um mês";
"lng_status_invisible" = "invisível";
"lng_status_lastseen_now" = "visto agora ";
"lng_status_lastseen_minutes" = "visto há {count:_not_used_|# minuto|# minutos}";
"lng_status_lastseen_hours" = "visto há {count:_not_used_|# hora|# horas} ";
"lng_status_lastseen_minutes#one" = "visto há {count} minuto";
"lng_status_lastseen_minutes#other" = "visto há {count} minutos";
"lng_status_lastseen_hours#one" = "visto há {count} hora";
"lng_status_lastseen_hours#other" = "visto há {count} horas";
"lng_status_lastseen_today" = "visto hoje às {time}";
"lng_status_lastseen_yesterday" = "visto ontem às {time}";
"lng_status_lastseen_date" = "visto por último {date}";
@@ -112,24 +118,43 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_status_connecting" = "conectando...";
"lng_chat_status_unaccessible" = "grupo inacessível";
"lng_chat_status_members" = "{count:nenhum membro|# membro|# membros}";
"lng_chat_status_members_online" = "{count:_not_used_|# membro|# membros}, {count_online:_not_used_|# online|# online}";
"lng_chat_status_no_members" = "nenhum membro";
"lng_chat_status_members#one" = "{count} membro";
"lng_chat_status_members#other" = "{count} membros";
"lng_chat_status_online#one" = "{count} online";
"lng_chat_status_online#other" = "{count} online";
"lng_chat_status_members_online" = "{members_count}, {online_count}";
"lng_channel_status" = "canal";
"lng_group_status" = "grupo";
"lng_channel_members_link" = "{count:_not_used_|# membro|# membros}";
"lng_channel_admins_link" = "{count:_not_used_|# administrador|# administradores}";
"lng_channel_members_link#one" = "{count} membro";
"lng_channel_members_link#other" = "{count} membros";
"lng_channel_admins_link#one" = "{count} administrador";
"lng_channel_admins_link#other" = "{count} administradores";
"lng_server_error" = "Erro interno do servidor.";
"lng_flood_error" = "Muitas tentativas. Tente novamente mais tarde.";
"lng_gif_error" = "Um erro ocorreu com a animação do GIF :(";
"lng_gif_error" = "Um erro ocorreu ao ler a animação do GIF :(";
"lng_edit_error" = "Você não pode editar essa mensagem";
"lng_join_channel_error" = "Desculpe, você já entrou em muitos canais e supergrupos. Por favor, saia de alguns antes de entrar.";
"lng_error_phone_flood" = "Desculpe, você apagou e recriou sua conta muitas vezes recentemente. Aguarde alguns dias antes de tentar novamente.";
"lng_error_start_minimized_passcoded" = "Você tem uma senha de bloqueio, então o aplicativo não pode iniciar minimizado. O app precisa de sua senha para iniciar.";
"lng_error_pinned_max" = "Você não pode fixar mais de {count:_not_used_|# conversa|# conversas} no topo.";
"lng_error_pinned_max#one" = "Você não pode fixar mais de {count} conversa no topo.";
"lng_error_pinned_max#other" = "Você não pode fixar mais de {count} conversas no topo.";
"lng_error_public_groups_denied" = "Infelizmente, você foi banido de participar em grupos públicos.\n{more_info}";
"lng_error_cant_edit_admin" = "Desculpe, você não pode editar permissões para esse admin.";
"lng_error_cant_add_member" = "Você não pode adicionar bots à esse grupo. Peça para um admin fazer isso.";
"lng_error_cant_add_bot" = "Esse bot não pode ser adicionado à grupos";
"lng_error_cant_add_admin_invite" = "Você não pode adicionar esse usuário como admin porque ele não é um membro do grupo e você não possui permissão para convidá-lo.";
"lng_error_cant_add_admin_unban" = "Você não pode adicionar esse usuário como admin porque ele está na lista negra e você não pode desbaní-lo.";
"lng_error_cant_ban_admin" = "Você não pode banir esse usuário porque ele é um admin do grupo e você não possui permissão para rebaixá-lo.";
"lng_sure_add_admin_invite" = "Esse usuário não é um membro do grupo. Adicioná-lo ao grupo e promover à administrador?";
"lng_sure_add_admin_unban" = "Esse usuário está restrito ou banido do grupo. Você tem certeza que quer o desbanir e o promovê-lo?";
"lng_sure_ban_admin" = "Esse usuário é um administrador do grupo. Você tem certeza que deseja restringi-lo?";
"lng_sure_ban_user_group" = "Banir {user} do grupo?";
"lng_sure_enable_socks" = "Você tem certeza que deseja habilitar esse proxy?\n\nServidor: {server}\nPorta: {port}\n\nVocê pode alterar seu servidor proxy depois em Configurações (Tipo de Conexão).";
"lng_sure_enable" = "Habilitar";
"lng_edit_deleted" = "Essa mensagem foi apagada";
"lng_edit_too_long" = "Sua mensagem está muito longa";
@@ -198,9 +223,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_signin_sure_reset" = "Atenção!\n\nVocê perderá todos seus chats e mensagens, juntamente com quaisquer mídias e arquivos!\n\nVocê deseja apagar sua conta?";
"lng_signin_reset" = "Apagar";
"lng_signin_reset_wait" = "Uma vez que a conta {phone_number} está ativa e protegida por senha, nós iremos desativá-la em 1 semana, por questões de segurança. Você pode cancelar esse processo a qualquer momento.\n\nVocê poderá restaurar sua conta em:\n{when}";
"lng_signin_reset_in_days" = "{count_days:0 dia|# dia|# dias} {count_hours:0 hora|# hora|# horas} {count_minutes:0 minuto|# minuto|# minutos}";
"lng_signin_reset_in_hours" = "{count_hours:0 hora|# hora|# horas} {count_minutes:0 minuto|# minuto|# minutos}";
"lng_signin_reset_in_minutes" = "{count_minutes:0 minuto|# minuto|# minutos}";
"lng_signin_reset_days#one" = "{count} dia";
"lng_signin_reset_days#other" = "{count} dias";
"lng_signin_reset_hours#one" = "{count} hora";
"lng_signin_reset_hours#other" = "{count} horas";
"lng_signin_reset_minutes#one" = "{count} minuto";
"lng_signin_reset_minutes#other" = "{count} minutos";
"lng_signin_reset_in_days" = "{days_count} {hours_count} {minutes_count}";
"lng_signin_reset_in_hours" = "{hours_count} {minutes_count}";
"lng_signin_reset_cancelled" = "Suas tentativas recentes de restaurar essa conta foram canceladas pelo usuário ativo. Tente novamente em 7 dias.";
"lng_signup_title" = "Suas Informações";
@@ -223,7 +253,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_settings_save" = "Salvar";
"lng_settings_upload" = "Definir Foto de Perfil";
"lng_settings_crop_profile" = "Selecione uma área para a foto do perfil";
"lng_settings_crop_profile" = "Selecione uma área para sua foto de perfil";
"lng_settings_uploading_photo" = "Carregando foto...";
"lng_settings_edit" = "Editar";
"lng_settings_drop_area_subtitle" = "para defini-la como sua foto";
@@ -241,10 +271,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_username_link" = "Esse link abre um chat com você:";
"lng_username_copied" = "Link copiado para área de transferência.";
"lng_bio_title" = "Editar sua bio";
"lng_bio_placeholder" = "Bio";
"lng_bio_about" = "Você pode adicionar algumas linhas sobre você. Qualquer um que ver seu perfil poderá ver esse texto.";
"lng_settings_section_info" = "Info";
"lng_settings_phone_number" = "Número de telefone:";
"lng_settings_username" = "Nome de usuário:";
"lng_settings_choose_username" = "Escolher nome de usuário";
"lng_settings_empty_bio" = "Nenhum";
"lng_settings_section_notify" = "Notificações";
"lng_settings_desktop_notify" = "Notificações na área de trabalho";
@@ -303,7 +338,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_backgrounds_header" = "Escolha o seu novo papel de parede";
"lng_theme_sure_keep" = "Manter esse tema de cores?";
"lng_theme_reverting" = "Revertendo ao antigo tema em {count:_not_used_|# segundo|# segundos}.";
"lng_theme_reverting#one" = "Revertendo ao antigo tema em {count} segundo.";
"lng_theme_reverting#other" = "Revertendo ao antigo tema em {count} segundos.";
"lng_theme_keep_changes" = "Manter alterações";
"lng_theme_revert" = "Reverter";
@@ -329,14 +365,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_local_storage_title" = "Armazenamento local";
"lng_settings_no_data_cached" = "Nada encontrado no cache!";
"lng_settings_images_cached" = "{count:_not_used_|# imagem|# imagens}, {size}";
"lng_settings_audios_cached" = "{count:_not_used_|# mensagem de voz|# mensagens de voz}, {size}";
"lng_settings_images_cached#one" = "{count} imagem, {size}";
"lng_settings_images_cached#other" = "{count} imagens, {size}";
"lng_settings_audios_cached#one" = "{count} mensagem de voz, {size}";
"lng_settings_audios_cached#other" = "{count} mensagens de voz, {size}";
"lng_local_storage_clear" = "Limpar tudo";
"lng_local_storage_clearing" = "Limpando...";
"lng_local_storage_cleared" = "Concluído!";
"lng_local_storage_clear_failed" = "Limpeza falhou :(";
"lng_settings_section_advanced_settings" = "Configurações Avançadas";
"lng_settings_enable_night_theme" = "Habilitar modo noturno";
"lng_settings_disable_night_theme" = "Desabilitar modo noturno";
"lng_passcode_remove_button" = "Remover";
@@ -348,8 +388,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_passcode_autolock" = "Autobloquear";
"lng_passcode_autolock_away" = "Autobloquear se ausente por:";
"lng_passcode_autolock_inactive" = "Autobloquear se inativo por:";
"lng_passcode_autolock_minutes" = "{count:_not_used_|# minuto|# minutos}";
"lng_passcode_autolock_hours" = "{count:_not_used_|# hora|# horas}";
"lng_passcode_autolock_minutes#one" = "{count} minuto";
"lng_passcode_autolock_minutes#other" = "{count} minutos";
"lng_passcode_autolock_hours#one" = "{count} hora";
"lng_passcode_autolock_hours#other" = "{count} horas";
"lng_passcode_enter_old" = "Insira sua senha atual";
"lng_passcode_enter_first" = "Insira uma senha";
"lng_passcode_enter_new" = "Insira sua nova senha";
@@ -455,32 +497,46 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_privacy_lastseen_title" = "Visto por último";
"lng_edit_privacy_lastseen_description" = "Você pode escolher quem vê o seu \"visto por último\":";
"lng_edit_privacy_lastseen_warning" = "Importante: você não poderá ver o horário de Visto por Último de pessoas com as quais você não compartilha o seu. Você verá um valor aproximado (recentemente, na última semana, no último mês).";
"lng_edit_privacy_lastseen_always" = "Sempre compartilhar com{count:| # usuário| # usuários}";
"lng_edit_privacy_lastseen_never" = "Nunca compartilhar com{count:| # usuário| # usuários}";
"lng_edit_privacy_lastseen_always_empty" = "Sempre compartilhar com";
"lng_edit_privacy_lastseen_always#one" = "Sempre compartilhar com {count} usuário";
"lng_edit_privacy_lastseen_always#other" = "Sempre compartilhar com {count} usuários";
"lng_edit_privacy_lastseen_never_empty" = "Nunca compartilhar com";
"lng_edit_privacy_lastseen_never#one" = "Nunca compartilhar com {count} usuário";
"lng_edit_privacy_lastseen_never#other" = "Nunca compartilhar com {count} usuários";
"lng_edit_privacy_lastseen_exceptions" = "Essas configurações irão sobrescrever os valores anteriores.";
"lng_edit_privacy_lastseen_always_title" = "Sempre compartilhar com";
"lng_edit_privacy_lastseen_never_title" = "Nunca compartilhar com";
"lng_edit_privacy_groups_title" = "Configurações de convite à grupos";
"lng_edit_privacy_groups_description" = "Você pode escolher quem pode te adicionar em grupos e canais com precisão:";
"lng_edit_privacy_groups_always" = "Sempre permitir{count:| # usuário| # usuários}";
"lng_edit_privacy_groups_never" = "Nunca permitir{count:| # usuário| # usuários}";
"lng_edit_privacy_groups_always_empty" = "Sempre permitir";
"lng_edit_privacy_groups_always#one" = "Sempre permitir {count} usuário";
"lng_edit_privacy_groups_always#other" = "Sempre permitir {count} usuários";
"lng_edit_privacy_groups_never_empty" = "Nunca permitir";
"lng_edit_privacy_groups_never#one" = "Nunca permitir {count} usuário";
"lng_edit_privacy_groups_never#other" = "Nunca permitir {count} usuários";
"lng_edit_privacy_groups_exceptions" = "Esses usuários poderão ou não te adicionar em grupos e canais dependendo de suas configurações.";
"lng_edit_privacy_groups_always_title" = "Sempre permitir";
"lng_edit_privacy_groups_never_title" = "Nunca permitir";
"lng_edit_privacy_calls_title" = "Chamadas";
"lng_edit_privacy_calls_description" = "Você pode restringir quem te ligou:";
"lng_edit_privacy_calls_always" = "Sempre permitir{count:| # usuário| # usuários}";
"lng_edit_privacy_calls_never" = "Nunca permitir{count:| # usuário| # usuários}";
"lng_edit_privacy_calls_always_empty" = "Sempre permitir";
"lng_edit_privacy_calls_always#one" = "Sempre permitir {count} usuário";
"lng_edit_privacy_calls_always#other" = "Sempre permitir {count} usuários";
"lng_edit_privacy_calls_never_empty" = "Nunca permitir";
"lng_edit_privacy_calls_never#one" = "Nunca permitir {count} usuário";
"lng_edit_privacy_calls_never#other" = "Nunca permitir {count} usuários";
"lng_edit_privacy_calls_exceptions" = "Esses usuários poderão ou não te ligar dependendo de suas configurações.";
"lng_edit_privacy_calls_always_title" = "Sempre permitir";
"lng_edit_privacy_calls_never_title" = "Nunca permitir";
"lng_self_destruct_title" = "Auto-destruição da conta";
"lng_self_destruct_description" = "Se você não ficar online ao menos uma vez nesse período, sua conta será apagada junto com seus grupos, mensagens e contatos.";
"lng_self_destruct_months" = "{count:_not_used_|# mês|# meses}";
"lng_self_destruct_years" = "{count:_not_used_|# ano|# anos}";
"lng_self_destruct_months#one" = "{count} mês";
"lng_self_destruct_months#other" = "{count} meses";
"lng_self_destruct_years#one" = "{count} ano";
"lng_self_destruct_years#other" = "{count} anos";
"lng_change_phone_title" = "Alterar número de telefone";
"lng_change_phone_description" = "Você pode alterar seu número do Telegram aqui.\nSua conta e todos os seus dados — mensagens,\nmídia, contatos, etc. serão movidos para\no novo número.\n\n[b]Importante[/b]: todos os contatos do Telegram terão\nseu [b]novo número[/b] adicionado às suas lista de contatos,\ndesde que eles tenham seu número antigo e você não\nos tenha bloqueado no Telegram.";
@@ -506,15 +562,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_invite_link_section" = "Link de convite";
"lng_profile_create_public_link" = "Criar link público";
"lng_profile_edit_public_link" = "Editar link público";
"lng_profile_search_members" = "Buscar membros";
"lng_profile_manage_admins" = "Gerenciar administradores";
"lng_profile_manage_blocklist" = "Gerenciar usuários bloqueados";
"lng_profile_common_groups" = "{count:_not_used_|# grupo|# grupos} em comum";
"lng_profile_manage_blocklist" = "Gerenciar usuários banidos";
"lng_profile_manage_restrictedlist" = "Gerenciar usuários restritos";
"lng_profile_recent_actions" = "Ações recentes";
"lng_profile_common_groups#one" = "{count} grupo em comum";
"lng_profile_common_groups#other" = "{count} grupos em comum";
"lng_profile_common_groups_section" = "Grupos em comum";
"lng_profile_participants_section" = "Membros";
"lng_profile_info_section" = "Info";
"lng_profile_mobile_number" = "Celular:";
"lng_profile_username" = "Nome de usuário:";
"lng_profile_link" = "Link:";
"lng_profile_bio" = "Bio:";
"lng_profile_add_contact" = "Adicionar Contato";
"lng_profile_edit_contact" = "Editar";
"lng_profile_enable_notifications" = "Notificações";
@@ -542,23 +603,30 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_delete_and_exit" = "Sair";
"lng_profile_kick" = "Remover";
"lng_profile_admin" = "administrador";
"lng_profile_edit_permissions" = "Editar";
"lng_profile_sure_kick" = "Remover {user} do grupo?";
"lng_profile_sure_kick_channel" = "Remover {user} do canal?";
"lng_profile_sure_kick_admin" = "Remover {user} dos administradores?";
"lng_profile_loading" = "Carregando...";
"lng_profile_shared_media" = "Mídia compartilhada";
"lng_profile_no_media" = "Nenhuma mídia nessa conversa.";
"lng_profile_photos" = "{count:_not_used_|# foto|# fotos}";
"lng_profile_photos#one" = "{count} foto";
"lng_profile_photos#other" = "{count} fotos";
"lng_profile_photos_header" = "Fotos";
"lng_profile_videos" = "{count:_not_used_|# vídeo|# vídeos}";
"lng_profile_videos#one" = "{count} vídeo";
"lng_profile_videos#other" = "{count} vídeos";
"lng_profile_videos_header" = "Vídeos";
"lng_profile_songs" = "{count:_not_used_|# áudio|# áudios}";
"lng_profile_songs#one" = "{count} áudio";
"lng_profile_songs#other" = "{count} áudios";
"lng_profile_songs_header" = "Áudios";
"lng_profile_files" = "{count:_not_used_|# arquivo|# arquivos}";
"lng_profile_files#one" = "{count} arquivo";
"lng_profile_files#other" = "{count} arquivos";
"lng_profile_files_header" = "Arquivos";
"lng_profile_audios" = "{count:_not_used_|# mensagem de voz|# mensagens de voz}";
"lng_profile_audios#one" = "{count} mensagem de voz";
"lng_profile_audios#other" = "{count} mensagens de voz";
"lng_profile_audios_header" = "Mensagens de voz";
"lng_profile_shared_links" = "{count:_not_used_|# link compartilhado|# links compartilhados}";
"lng_profile_shared_links#one" = "{count} link compartilhado";
"lng_profile_shared_links#other" = "{count} links compartilhados";
"lng_profile_shared_links_header" = "Links compartilhados";
"lng_profile_copy_phone" = "Copiar Número de Telefone";
"lng_profile_copy_fullname" = "Copiar Nome";
@@ -578,16 +646,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_report_button" = "Reportar";
"lng_report_thanks" = "Obrigado! O relatório será revisado pela nossa equipe em breve.";
"lng_channel_add_admins" = "Novo administrador";
"lng_channel_add_members" = "Adicionar membros";
"lng_channel_add_banned" = "Banir usuário";
"lng_channel_add_restricted" = "Restringir usuário";
"lng_channel_members" = "Membros";
"lng_channel_only_last_shown" = "Somente os últimos {count:_not_used_|# membro é|# membros são} exibidos";
"lng_channel_only_last_shown#one" = "Somente o último {count} membro é exibido aqui";
"lng_channel_only_last_shown#other" = "Somente os últimos {count} membros são exibidos aqui";
"lng_channel_admins" = "Administradores";
"lng_channel_add_admin" = "Adicionar Administrador";
"lng_channel_admin_sure" = "Adicionar {user} aos administradores?";
"lng_channel_admins_too_much" = "Você atingiu o limite de administradores. Remova um dos administradores primeiro.";
"lng_channel_admin_status_creator" = "Criador";
"lng_channel_admin_status_promoted_by" = "Promovido por {user}";
"lng_channel_admin_status_not_admin" = "Não é administrador";
"lng_group_blocked_list_about" = "Usuários bloqueados são removidos do grupo e só podem voltar se convidados por um admin.\nLinks de convite não funcionam para eles.";
"lng_group_blocked_list_about" = "Usuários banidos são removidos do grupo e só podem voltar se convidados por um admin.\nLinks de convite não funcionam para eles.";
"lng_chat_all_members_admins" = "Todos São Admins";
"lng_chat_about_all_admins" = "Todos podem adicionar novos membros, editar nome e foto do grupo.";
@@ -595,7 +667,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_participant_filter" = "Buscar";
"lng_participant_invite" = "Convidar";
"lng_participant_invite_sorry" = "Desculpe, você só pode adicionar os primeiros {count:_not_used|# membro|# membros} ao canal.\n\nDe agora em diante, precisarão do link de convite para entrar.";
"lng_participant_invite_sorry#one" = "Desculpe, você só pode adicionar\no primeiro {count} membro ao canal.\n\nDe agora em diante, precisarão\ndo link de convite para entrar.";
"lng_participant_invite_sorry#other" = "Desculpe, você só pode adicionar\nos primeiros {count} membros ao canal.\n\nDe agora em diante, precisarão\ndo link de convite para entrar.";
"lng_create_group_back" = "Voltar";
"lng_create_group_next" = "Próximo";
"lng_create_group_create" = "Criar";
@@ -637,8 +710,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_message_empty" = "Mensagem Vazia";
"lng_message_unsupported" = "Essa mensagem não é suportada em sua versão do Telegram Desktop. Atualize para a última versão nas Configurações ou instale por aqui {link}";
"lng_duration_seconds" = "{count:_not_used_|# segundo|# segundos}";
"lng_duration_minutes_seconds" = "{count_minutes:# min|# min|# min} {count_seconds:# seg|# seg|# seg}";
"lng_duration_seconds#one" = "{count} segundo";
"lng_duration_seconds#other" = "{count} segundos";
"lng_duration_minsec_minutes#one" = "{count} min";
"lng_duration_minsec_minutes#other" = "{count} min";
"lng_duration_minsec_seconds#one" = "{count} seg";
"lng_duration_minsec_seconds#other" = "{count} seg";
"lng_duration_minutes_seconds" = "{minutes_count} {seconds_count}";
"lng_action_add_user" = "{from} adicionou {user}";
"lng_action_add_users_many" = "{from} adicionou {users}";
@@ -676,18 +754,33 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_action_pinned_media_sticker" = "um sticker";
"lng_action_pinned_media_emoji_sticker" = "um {emoji} sticker";
"lng_action_pinned_media_game" = "o jogo «{game}»";
"lng_action_game_score" = "{from} marcou {count:# pontos|# ponto|# pontos} em {game}";
"lng_action_game_you_scored" = "Você marcou {count:# pontos|# ponto|# pontos} em {game}";
"lng_action_game_score_no_game" = "{from} marcou {count:# pontos|# ponto|# pontos}";
"lng_action_game_you_scored_no_game" = "Você marcou {count:# pontos|# ponto|# pontos}";
"lng_action_game_score#one" = "{from} marcou {count} ponto em {game}";
"lng_action_game_score#other" = "{count} marcou {count} pontos em {game}";
"lng_action_game_you_scored#one" = "Você marcou {count} ponto em {game}";
"lng_action_game_you_scored#other" = "Você marcou {count} pontos em {game}";
"lng_action_game_score_no_game#one" = "{from} marcou {count} ponto";
"lng_action_game_score_no_game#other" = "{from} marcou {count} pontos";
"lng_action_game_you_scored_no_game#one" = "Você marcou {count} ponto";
"lng_action_game_you_scored_no_game#other" = "Você marcou {count} pontos";
"lng_action_payment_done" = "Você transferiu com sucesso {amount} para {user}";
"lng_action_payment_done_for" = "Você transferiu com sucesso {amount} para {user} por {invoice}";
"lng_action_took_screenshot" = "{from} realizou uma captura de tela!";
"lng_action_you_took_screenshot" = "Você realizou uma captura de tela!";
"lng_profile_migrate_reached" = "{count:_not_used_|# membro|# membros} limite alcançado";
"lng_ttl_photo_received" = "{from} te enviou uma foto auto-destrutiva. Por favor, veja em seu celular.";
"lng_ttl_photo_sent" = "Você enviou uma foto auto-destrutiva.";
"lng_ttl_photo_expired" = "A foto expirou";
"lng_ttl_video_received" = "{from} te enviou um vídeo auto-destrutivo. Por favor, veja em seu celular.";
"lng_ttl_video_sent" = "Você enviou um vídeo auto-destrutivo.";
"lng_ttl_video_expired" = "O vídeo expirou";
"lng_profile_migrate_reached#one" = "{count} limite de membro alcançado";
"lng_profile_migrate_reached#other" = "{count} limite de membros alcançado";
"lng_profile_migrate_body" = "Para ir além desse limite, você pode converter seu grupo em um supergrupo.";
"lng_profile_migrate_learn_more" = "Leia mais »";
"lng_profile_migrate_about" = "Se você deseja ir além do limite, pode converter seu grupo em um supergrupo. Nos supergrupos:";
"lng_profile_migrate_feature1" = "— O limite de membros são {count:_not_used_|# usuário|# usuários}";
"lng_profile_migrate_feature1#one" = "— O limite de membros é de {count} usuário";
"lng_profile_migrate_feature1#other" = "— O limite de membros é de {count} usuários";
"lng_profile_migrate_feature2" = "— Novos membros podem ver todo o histórico";
"lng_profile_migrate_feature3" = "— Administradores podem apagar mensagens para todos";
"lng_profile_migrate_feature4" = "— Notificações silenciadas por padrão";
@@ -698,11 +791,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_profile_convert_about" = "Em supergrupos:";
"lng_profile_convert_feature1" = "— Novos membros podem ver todo o histórico";
"lng_profile_convert_feature2" = "— Mensagens apagadas desaparecerão para todos";
"lng_profile_convert_feature3" = "— Admins podem ficar mensagens importantes";
"lng_profile_convert_feature3" = "— Admins podem fixar mensagens importantes";
"lng_profile_convert_feature4" = "— Criador pode definir um link público para o grupo";
"lng_profile_convert_warning" = "{bold_start}Nota:{bold_end} Essa ação não pode ser desfeita";
"lng_profile_convert_confirm" = "Converter";
"lng_profile_add_more_after_upgrade" = "Você pode adicionar até {count:_not_used_|# membro|# membros} depois de converter seu grupo a um supergrupo.";
"lng_profile_add_more_after_upgrade#one" = "Você pode adicionar até {count} membro depois de converter seu grupo a um supergrupo.";
"lng_profile_add_more_after_upgrade#other" = "Você pode adicionar até {count} membros depois de converter seu grupo a um supergrupo.";
"lng_channel_not_accessible" = "Desculpe, esse canal não está acessível.";
"lng_group_not_accessible" = "Desculpe, esse grupo não está acessível.";
@@ -717,7 +811,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_group_invite_want_join_channel" = "Você deseja entrar no canal «{title}»?";
"lng_group_invite_join" = "Entrar";
"lng_group_invite_members" = "{count:_not_used_|# membro|# membros}, entre eles:";
"lng_group_invite_members#one" = "{count} membro, entre eles:";
"lng_group_invite_members#other" = "{count} membros, entre eles:";
"lng_group_invite_link" = "Link de convite:";
"lng_group_invite_create" = "Criar um link de convite";
@@ -730,6 +825,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_channel_public_link_copied" = "Link copiado para área de transferência.";
"lng_forwarded" = "Encaminhado de {user}";
"lng_forwarded_date" = "Original: {date}";
"lng_forwarded_channel" = "Encaminhado de {channel}";
"lng_forwarded_via" = "Encaminhado de {user} via {inline_bot}";
"lng_forwarded_channel_via" = "Encaminhado de {channel} via {inline_bot}";
@@ -809,7 +905,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_stickers_you_have" = "Gerenciar e reordenar os pacotes de sticker";
"lng_stickers_featured" = "Stickers Populares";
"lng_stickers_return" = "Desfazer";
"lng_stickers_count" = "{count:Carregando...|# sticker|# stickers}";
"lng_stickers_count#one" = "{count} sticker";
"lng_stickers_count#other" = "{count} stickers";
"lng_stickers_masks_pack" = "Esse é um pacote de máscaras. Você pode usá-las no editor de fotos em nossos aplicativos para celular.";
"lng_in_dlg_photo" = "Foto";
@@ -839,6 +936,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_cant_invite_banned" = "Apenas um administrador pode adicionar esse usuário.";
"lng_cant_invite_privacy" = "Você não pode adicionar esse usuário em grupos devido as configurações de privacidade.";
"lng_cant_invite_privacy_channel" = "Você não pode adicionar esse usuário em canais devido as configurações de privacidade.";
"lng_cant_delete_group#one" = "Desculpe, atualmente não é possível apagar comunidades que tenham mais de {count} membro. Por favor, contate o suporte do Telegram se você precisa apagar esse grupo.";
"lng_cant_delete_group#other" = "Desculpe, atualmente não é possível apagar comunidades que tenham mais de {count} membros. Por favor, contate o suporte do Telegram se você precisa apagar esse grupo.";
"lng_cant_delete_channel#one" = "Desculpe, atualmente não é possível apagar comunidades que tenham mais de {count} membro. Por favor, contate o suporte do Telegram se você precisa apagar esse canal.";
"lng_cant_delete_channel#other" = "Desculpe, atualmente não é possível apagar comunidades que tenham mais de {count} membros. Por favor, contate o suporte do Telegram se você precisa apagar esse canal.";
"lng_cant_do_this" = "Essa ação não está disponível.";
"lng_send_button" = "Enviar";
@@ -884,11 +985,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_typing" = "escrevendo";
"lng_user_typing" = "{user} está escrevendo";
"lng_users_typing" = "{user} e {second_user} estão escrevendo";
"lng_many_typing" = "{count:_not_used_|# está|# estão} escrevendo";
"lng_many_typing#one" = "{count} está escrevendo";
"lng_many_typing#other" = "{count} estão escrevendo";
"lng_playing_game" = "jogando";
"lng_user_playing_game" = "{user} está jogando";
"lng_users_playing_game" = "{user} e {second_user} estão jogando";
"lng_many_playing_game" = "{count:_not_used_|# está|# estão} jogando";
"lng_many_playing_game#one" = "{count} está jogando";
"lng_many_playing_game#other" = "{count} estão jogando";
"lng_send_action_record_video" = "gravando um vídeo";
"lng_user_action_record_video" = "{user} está gravando um vídeo";
"lng_send_action_upload_video" = "enviando um vídeo";
@@ -909,7 +1012,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_user_action_geo_location" = "{user} está escolhendo uma localização";
"lng_send_action_choose_contact" = "escolhendo um contato";
"lng_user_action_choose_contact" = "{user} está escolhendo um contato";
"lng_unread_bar" = "{count:_not_used_|# mensagem não lida|# mensagens não lidas}";
"lng_unread_bar#one" = "{count} mensagem não lida";
"lng_unread_bar#other" = "{count} mensagens não lidas";
"lng_maps_point" = "Localização";
"lng_save_photo" = "Salvar imagem";
@@ -932,7 +1036,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_from_top" = "Desafixar do topo";
"lng_context_promote_admin" = "Promover a administrador";
"lng_context_remove_admin" = "Remover dos administradores";
"lng_context_edit_permissions" = "Editar permissões";
"lng_context_restrict_user" = "Restringir usuário";
"lng_context_remove_from_group" = "Remover do grupo";
"lng_context_copy_link" = "Copiar Link";
@@ -970,31 +1075,41 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_context_unpin_msg" = "Desafixar Mensagem";
"lng_context_cancel_upload" = "Cancelar Envio";
"lng_context_copy_selected" = "Copiar Texto Selecionado";
"lng_context_copy_selected_items" = "Copiar Seleção como Texto";
"lng_context_forward_selected" = "Encaminhar Selecionado";
"lng_context_delete_selected" = "Apagar Selecionado";
"lng_context_clear_selection" = "Limpar Seleção";
"lng_really_send_image" = "Você deseja enviar essa imagem?";
"lng_really_send_file" = "Você deseja enviar esse arquivo?";
"lng_really_share_contact" = "Você deseja compartilhar esse contato?";
"lng_send_images_compress" = "Comprimir {count:_not_used_|imagem|imagens}";
"lng_send_images_compress#one" = "Comprimir imagem";
"lng_send_images_compress#other" = "Comprimir imagens";
"lng_send_image_non_local" = "Não foi possível enviar um arquivo não local: {name}";
"lng_send_image_empty" = "Não foi possível enviar um arquivo vazio: {name}";
"lng_send_image_too_large" = "Não foi possível enviar um arquivo, por ser maior que 1500 MB: {name}";
"lng_send_folder" = "Não pude enviar «{name}» porque é um diretório :(";
"lng_send_images_selected" = "{count:_not_used_|# imagem selecionada|# imagens selecionadas}";
"lng_send_photos" = "Enviar {count:_not_used_|# foto|# fotos}";
"lng_send_files_selected" = "{count:_not_used_|# arquivo selecionado|# arquivos selecionados}";
"lng_send_files" = "Enviar {count:_not_used_|# arquivo|# arquivos}";
"lng_send_images_selected#one" = "{count} imagem selecionada";
"lng_send_images_selected#other" = "{count} imagens selecionadas";
"lng_send_photos#one" = "Enviar {count} foto";
"lng_send_photos#other" = "Enviar {count} fotos";
"lng_send_files_selected#one" = "{count} arquivo selecionado";
"lng_send_files_selected#other" = "{count} arquivos selecionados";
"lng_send_files#one" = "Enviar {count} arquivo";
"lng_send_files#other" = "Enviar {count} arquivos";
"lng_forward_choose" = "Escolher recipiente...";
"lng_forward_cant" = "Desculpe, não há como encaminhar aqui :(";
"lng_forward_confirm" = "Encaminhar para {recipient}?";
"lng_forward_share_contact" = "Compartilhar contato com {recipient}?";
"lng_forward_share_cant" = "Não há como compartilhar um contato aqui :(";
"lng_forward_send_file_confirm" = "Enviar «{name}» para {recipient}";
"lng_forward_send_files_confirm" = "Enviar arquivos selecionados para {recipient}?";
"lng_forward_send_files_cant" = "Não há como compartilhar um mídias aqui :(";
"lng_forward_send" = "Enviar";
"lng_forward_messages" = "{count:_not_used_|Mensagem encaminhada|# mensagens encaminhadas}";
"lng_forwarding_from" = "{user} e {count:_not_used_|# outro|# outros}";
"lng_forward_messages#one" = "{count} mensagem encaminhada";
"lng_forward_messages#other" = "{count} mensagens encaminhadas";
"lng_forwarding_from#one" = "{user} e {count} outro";
"lng_forwarding_from#other" = "{user} e {count} outros";
"lng_forwarding_from_two" = "{user} e {second_user}";
"lng_inline_switch_choose" = "Escolher conversa...";
"lng_inline_switch_cant" = "Desculpe, não há como escrever aqui :(";
@@ -1016,6 +1131,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_edit_contact_title" = "Editar nome de contato";
"lng_edit_channel_title" = "Editar Canal";
"lng_edit_sign_messages" = "Assinar mensagens";
"lng_edit_group_who_invites" = "Quem pode adicionar membros";
"lng_edit_group_invites_everybody" = "Todos os membros";
"lng_edit_group_invites_only_admins" = "Somente administradores";
"lng_edit_group" = "Editar grupo";
"lng_edit_self_title" = "Editar seu nome";
"lng_confirm_contact_data" = "Novo Contato";
@@ -1040,15 +1158,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_selected_clear" = "Cancelar";
"lng_selected_delete" = "Apagar";
"lng_selected_forward" = "Encaminhar";
"lng_selected_count" = "{count:_not_used_|# mensagem|# mensagens}";
"lng_selected_count#one" = "{count} mensagem";
"lng_selected_count#other" = "{count} mensagens";
"lng_selected_cancel_sure_this" = "Cancelar envio?";
"lng_selected_upload_stop" = "Parar";
"lng_selected_delete_sure_this" = "Você deseja apagar essa mensagem?";
"lng_selected_delete_sure" = "Você deseja apagar {count:_not_used_|# mensagem|# mensagens}?";
"lng_selected_delete_sure#one" = "Você deseja apagar {count} mensagem?";
"lng_selected_delete_sure#other" = "Você deseja apagar {count} mensagens?";
"lng_delete_photo_sure" = "Você deseja apagar essa foto?";
"lng_delete_for_everyone_hint" = "Isso irá apagar {count:_not_used_|a mensagem|as mensagens} para todos nessa conversa.";
"lng_delete_for_me_chat_hint" = "Isso irá apagar {count:_not_used_|a mensagem|as mensagens} somente para você, não para os outros participantes da conversa.";
"lng_delete_for_me_hint" = "Isso irá apagar {count:_not_used_|a mensagem|as mensagens} apenas para você.";
"lng_delete_for_everyone_hint#one" = "Isso irá apagar para todos nessa conversa.";
"lng_delete_for_everyone_hint#other" = "Isso irá apagar para todos nessa conversa.";
"lng_delete_for_me_chat_hint#one" = "Isso apagará somente para você, não para os outros participantes da conversa.";
"lng_delete_for_me_chat_hint#other" = "Isso irá apagar somente para você, não para os outros participantes da conversa.";
"lng_delete_for_me_hint#one" = "Isso irá apagar somente para você.";
"lng_delete_for_me_hint#other" = "Isso irá apagar somente para você.";
"lng_delete_for_everyone_check" = "Apagar para todos";
"lng_delete_for_other_check" = "Apagar para {user}";
"lng_box_delete" = "Apagar";
@@ -1060,7 +1183,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_about_text_3" = "Visite as {faq_open}Perguntas Frequentes{faq_close} para mais informações.";
"lng_about_done" = "Concluído";
"lng_search_found_results" = "{count:Nenhuma mensagem encontrada|Encontrada # mensagem|Encontradas # mensagens}";
"lng_search_no_results" = "Nenhuma mensagem encontrada";
"lng_search_found_results#one" = "Encontrada {count} mensagem";
"lng_search_found_results#other" = "Encontradas {count} mensagens";
"lng_search_global_results" = "Resultados da busca global";
"lng_media_save_progress" = "{ready} de {total} {mb}";
@@ -1169,11 +1294,133 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_player_message_yesterday" = "Ontem às {time}";
"lng_player_message_date" = "{date} às {time}";
"lng_rights_edit_admin" = "Editar administrador";
"lng_rights_edit_admin_header" = "O que esse administrador pode fazer?";
"lng_rights_about_add_admins_yes" = "Esse admin poderá adicionar novos admins com as mesmas (ou mais limitadas) permissões.";
"lng_rights_about_add_admins_no" = "Esse admin não poderá adicionar novos admins.";
"lng_rights_about_admin_cant_edit" = "Você não pode editar as permissões desse admin.";
"lng_rights_user_restrictions" = "Restrições do usuário";
"lng_rights_user_restrictions_header" = "O que esse usuário pode fazer?";
"lng_rights_channel_info" = "Alterar info do canal";
"lng_rights_channel_post" = "Postar mensagens";
"lng_rights_channel_edit" = "Editar mensagens de outros";
"lng_rights_channel_delete" = "Apagar mensagens de outros";
"lng_rights_group_info" = "Alterar info do grupo";
"lng_rights_group_ban" = "Banir usuários";
"lng_rights_group_invite_link" = "Convidar usuários via link";
"lng_rights_group_invite" = "Adicionar usuários";
"lng_rights_group_pin" = "Fixar mensagens";
"lng_rights_group_delete" = "Apagar mensagens";
"lng_rights_add_admins" = "Adicionar novos admins";
"lng_rights_chat_read" = "Ler mensagens";
"lng_rights_chat_send_text" = "Enviar mensagens";
"lng_rights_chat_send_media" = "Enviar mídias";
"lng_rights_chat_send_stickers" = "Enviar stickers e GIFs";
"lng_rights_chat_send_links" = "Preview de links";
"lng_rights_chat_banned_until_header" = "Restringir até";
"lng_rights_chat_banned_forever" = "Sempre";
"lng_rights_chat_banned_day#one" = "Por {count} dia";
"lng_rights_chat_banned_day#other" = "Por {count} dias";
"lng_rights_chat_banned_week#one" = "Por {count} semana";
"lng_rights_chat_banned_week#other" = "Por {count} semanas";
"lng_rights_chat_banned_custom" = "Personalizado";
"lng_rights_chat_banned_custom_date" = "Até {date}";
"lng_rights_chat_banned_block" = "Banir e remover do grupo";
"lng_restricted_send_message" = "Os administradores do grupo restringiram você de escrever aqui.";
"lng_restricted_send_media" = "Os administradores do grupo restringiram você de enviar mídias aqui.";
"lng_restricted_send_stickers" = "Desculpe, os administradores do grupo te restringiram do envio de stickers.";
"lng_restricted_send_gifs" = "Desculpe, os administradores do grupo te restringiram do envio de GIFs.";
"lng_restricted_send_inline" = "Os administradores do grupo restringiram você de usar bots integrados aqui.";
"lng_restricted_list_title" = "Usuários restritos";
"lng_banned_list_title" = "Usuários banidos";
"lng_admin_log_title_all" = "Todas as ações";
"lng_admin_log_title_selected" = "Ações selecionadas";
"lng_admin_log_filter" = "Filtro";
"lng_admin_log_filter_title" = "Filtro";
"lng_admin_log_filter_all_actions" = "Todas as ações";
"lng_admin_log_filter_restrictions" = "Novas restrições";
"lng_admin_log_filter_admins_new" = "Novos admins";
"lng_admin_log_filter_members_new" = "Novos membros";
"lng_admin_log_filter_info_group" = "Info do grupo";
"lng_admin_log_filter_info_channel" = "Info do canal";
"lng_admin_log_filter_messages_deleted" = "Mensagens apagadas";
"lng_admin_log_filter_messages_edited" = "Mensagens editadas";
"lng_admin_log_filter_messages_pinned" = "Mensagens fixadas";
"lng_admin_log_filter_members_removed" = "Membros saindo";
"lng_admin_log_filter_all_admins" = "Todos usuários e admins";
"lng_admin_log_about" = "O que é isso?";
"lng_admin_log_about_text" = "Essa é uma lista de todas as ações tomadas pelos membros e admins do grupo nas últimas 48 horas.";
"lng_admin_log_no_results_title" = "Nenhuma ação encontrada";
"lng_admin_log_no_results_text" = "Nenhuma ação correspondente foi encontrado.";
"lng_admin_log_no_results_search_text" = "Nenhuma ação que contenha \"{query}\" foi encontrada.";
"lng_admin_log_no_events_title" = "Ainda não há ações aqui.";
"lng_admin_log_no_events_text" = "Nenhuma ação foi tomada pelos\nmembros e admins do grupo\nnas últimas 48 horas.";
"lng_admin_log_empty_text" = "Vazio";
"lng_admin_log_changed_title_group" = "{from} alterou o título do grupo para «{title}»";
"lng_admin_log_changed_title_channel" = "{from} alterou o título do canal para «{title}»";
"lng_admin_log_changed_description_group" = "{from} editou a descrição do grupo:";
"lng_admin_log_removed_description_group" = "{from} removeu a descrição do grupo";
"lng_admin_log_changed_description_channel" = "{from} editou a descrição do canal:";
"lng_admin_log_removed_description_channel" = "{from} removeu a descrição do canal";
"lng_admin_log_previous_description" = "Descrição prévia";
"lng_admin_log_changed_link_group" = "{from} alterou o link do grupo";
"lng_admin_log_removed_link_group" = "{from} removeu o link do grupo";
"lng_admin_log_changed_link_channel" = "{from} alterou o link do canal:";
"lng_admin_log_removed_link_channel" = "{from} removeu o link do canal";
"lng_admin_log_previous_link" = "Link prévio";
"lng_admin_log_changed_photo_group" = "{from} alterou a foto do grupo";
"lng_admin_log_changed_photo_channel" = "{from} alterou a foto do canal";
"lng_admin_log_removed_photo_group" = "{from} removeu a foto do grupo";
"lng_admin_log_removed_photo_channel" = "{from} removeu a foto do canal";
"lng_admin_log_invites_enabled" = "{from} habilitou convites ao grupo";
"lng_admin_log_invites_disabled" = "{from} desabilitou convites ao grupo";
"lng_admin_log_signatures_enabled" = "{from} assinaturas habilitadas";
"lng_admin_log_signatures_disabled" = "{from} assinaturas desabilitadas";
"lng_admin_log_pinned_message" = "{from} fixou a mensagem:";
"lng_admin_log_unpinned_message" = "{from} desafixou a mensagem";
"lng_admin_log_edited_caption" = "{from} editou a legenda:";
"lng_admin_log_removed_caption" = "{from} removeu a legenda";
"lng_admin_log_previous_caption" = "Legenda original";
"lng_admin_log_edited_message" = "{from} editou a mensagem:";
"lng_admin_log_previous_message" = "Mensagem original";
"lng_admin_log_deleted_message" = "{from} apagou a mensagem:";
"lng_admin_log_participant_joined" = "{from} entrou no grupo";
"lng_admin_log_participant_joined_channel" = "{from} entrou no canal";
"lng_admin_log_participant_left" = "{from} deixou o grupo";
"lng_admin_log_participant_left_channel" = "{from} saiu do canal";
"lng_admin_log_invited" = "convidou {user}";
"lng_admin_log_banned" = "baniu {user}";
"lng_admin_log_restricted" = "alterou as restrições de {user} {until}";
"lng_admin_log_promoted" = "alterou os privilégios de {user}";
"lng_admin_log_user_with_username" = "{name} ({mention})";
"lng_admin_log_restricted_forever" = "indefinidamente";
"lng_admin_log_restricted_until" = "até {data}";
"lng_admin_log_banned_view_messages" = "Ler mensagens";
"lng_admin_log_banned_send_messages" = "Enviar mensagens";
"lng_admin_log_banned_send_media" = "Enviar mídias";
"lng_admin_log_banned_send_stickers" = "Enviar stickers e GIFs";
"lng_admin_log_banned_embed_links" = "Preview de links";
"lng_admin_log_admin_change_info" = "Alterar info";
"lng_admin_log_admin_post_messages" = "Postar mensagens";
"lng_admin_log_admin_edit_messages" = "Editar mensagens";
"lng_admin_log_admin_delete_messages" = "Apagar mensagens";
"lng_admin_log_admin_ban_users" = "Banir usuários";
"lng_admin_log_admin_invite_users" = "Adicionar usuários";
"lng_admin_log_admin_invite_link" = "Convidar usuários via link";
"lng_admin_log_admin_pin_messages" = "Fixar mensagens";
"lng_admin_log_admin_add_admins" = "Adicionar novos admins";
// Not used
"lng_topbar_info" = "Info";
"lng_profile_group_info" = "Informação do grupo";
"lng_profile_channel_info" = "Info do Canal";
"lng_channel_add_admins" = "Novo administrador";
// Wnd specific

Binary file not shown.

View File

@@ -8,6 +8,7 @@
<file alias="art/logo_256.png">../art/logo_256.png</file>
<file alias="art/logo_256_no_margin.png">../art/logo_256_no_margin.png</file>
<file alias="art/sunrise.jpg">../art/sunrise.jpg</file>
<file alias="night.tdesktop-theme">../night.tdesktop-theme</file>
</qresource>
<qresource prefix="/sounds">
<file alias="msg_incoming.mp3">../sounds/msg_incoming.mp3</file>

View File

@@ -106,6 +106,9 @@ new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long =
http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait;
ipPort ipv4:int port:int = IpPort;
help.configSimple#d997c3c5 date:int expires:int dc_id:int ip_port_list:Vector<ipPort> = help.ConfigSimple;
---functions---
rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
@@ -152,17 +155,16 @@ inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile
inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile;
inputMediaEmpty#9664f57f = InputMedia;
inputMediaUploadedPhoto#630c9af1 flags:# file:InputFile caption:string stickers:flags.0?Vector<InputDocument> = InputMedia;
inputMediaPhoto#e9bfb4f3 id:InputPhoto caption:string = InputMedia;
inputMediaUploadedPhoto#2f37e231 flags:# file:InputFile caption:string stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
inputMediaPhoto#81fa373a flags:# id:InputPhoto caption:string ttl_seconds:flags.0?int = InputMedia;
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
inputMediaContact#a6e45987 phone_number:string first_name:string last_name:string = InputMedia;
inputMediaUploadedDocument#d070f1e9 flags:# file:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string stickers:flags.0?Vector<InputDocument> = InputMedia;
inputMediaUploadedThumbDocument#50d88cae flags:# file:InputFile thumb:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string stickers:flags.0?Vector<InputDocument> = InputMedia;
inputMediaDocument#1a77f29c id:InputDocument caption:string = InputMedia;
inputMediaUploadedDocument#e39621fd flags:# file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
inputMediaDocument#5acb668e flags:# id:InputDocument caption:string ttl_seconds:flags.0?int = InputMedia;
inputMediaVenue#2827a81a geo_point:InputGeoPoint title:string address:string provider:string venue_id:string = InputMedia;
inputMediaGifExternal#4843b0fd url:string q:string = InputMedia;
inputMediaPhotoExternal#b55f4f18 url:string caption:string = InputMedia;
inputMediaDocumentExternal#e5e9607c url:string caption:string = InputMedia;
inputMediaPhotoExternal#922aec1 flags:# url:string caption:string ttl_seconds:flags.0?int = InputMedia;
inputMediaDocumentExternal#b6f74335 flags:# url:string caption:string ttl_seconds:flags.0?int = InputMedia;
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
inputMediaInvoice#92153685 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string start_param:string = InputMedia;
@@ -216,11 +218,11 @@ userStatusLastMonth#77ebc742 = UserStatus;
chatEmpty#9ba2d800 id:int = Chat;
chat#d91cdd54 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true admins_enabled:flags.3?true admin:flags.4?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel = Chat;
chatForbidden#7328bdb id:int title:string = Chat;
channel#a14dca52 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true editor:flags.3?true moderator:flags.4?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string = Chat;
channelForbidden#8537784f flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string = Chat;
channel#cb44b1c flags:# creator:flags.0?true left:flags.2?true editor:flags.3?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChannelAdminRights banned_rights:flags.15?ChannelBannedRights = Chat;
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#2e02a614 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> = ChatFull;
channelFull#c3d5512f flags:# can_view_participants:flags.3?true can_set_username:flags.6?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int = ChatFull;
channelFull#95cb5f57 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int = ChatFull;
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
@@ -233,15 +235,15 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto;
messageEmpty#83e5de54 id:int = Message;
message#c09be45f flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int = Message;
message#90dddc11 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string = Message;
messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
messageMediaPhoto#3d8ce53d photo:Photo caption:string = MessageMedia;
messageMediaPhoto#b5223b0f flags:# photo:flags.0?Photo caption:flags.1?string ttl_seconds:flags.2?int = MessageMedia;
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
messageMediaContact#5e7d2f39 phone_number:string first_name:string last_name:string user_id:int = MessageMedia;
messageMediaUnsupported#9f84f49e = MessageMedia;
messageMediaDocument#f3e02ea8 document:Document caption:string = MessageMedia;
messageMediaDocument#7c4414d3 flags:# document:flags.0?Document caption:flags.1?string ttl_seconds:flags.2?int = MessageMedia;
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
messageMediaVenue#7912b71f geo:GeoPoint title:string address:string provider:string venue_id:string = MessageMedia;
messageMediaGame#fdb19008 game:Game = MessageMedia;
@@ -264,6 +266,7 @@ messageActionGameScore#92a72876 game_id:long score:int = MessageAction;
messageActionPaymentSentMe#8f31b327 flags:# currency:string total_amount:long payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string charge:PaymentCharge = MessageAction;
messageActionPaymentSent#40699cd0 currency:string total_amount:long = MessageAction;
messageActionPhoneCall#80e11a7f flags:# call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction;
messageActionScreenshotTaken#4792929b = MessageAction;
dialog#66ffba14 flags:# pinned:flags.2?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
@@ -326,7 +329,7 @@ contacts.link#3ace484c my_link:ContactLink foreign_link:ContactLink user:User =
contacts.contactsNotModified#b74ba9d2 = contacts.Contacts;
contacts.contacts#6f8b8cb2 contacts:Vector<Contact> users:Vector<User> = contacts.Contacts;
contacts.importedContacts#ad524315 imported:Vector<ImportedContact> retry_contacts:Vector<long> users:Vector<User> = contacts.ImportedContacts;
contacts.importedContacts#77d01c3b imported:Vector<ImportedContact> popular_invites:Vector<PopularContact> retry_contacts:Vector<long> users:Vector<User> = contacts.ImportedContacts;
contacts.blocked#1c138d15 blocked:Vector<ContactBlocked> users:Vector<User> = contacts.Blocked;
contacts.blockedSlice#900802a1 count:int blocked:Vector<ContactBlocked> users:Vector<User> = contacts.Blocked;
@@ -420,6 +423,8 @@ updateBotWebhookJSONQuery#9b9240a6 query_id:long data:DataJSON timeout:int = Upd
updateBotShippingQuery#e0cdc940 query_id:long user_id:int payload:bytes shipping_address:PostAddress = Update;
updateBotPrecheckoutQuery#5d2f3aa9 flags:# query_id:long user_id:int payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string currency:string total_amount:long = Update;
updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update;
updateLangPackTooLong#10c2404b = Update;
updateLangPack#56022f4d difference:LangPackDifference = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@@ -442,11 +447,11 @@ photos.photosSlice#15051f54 count:int photos:Vector<Photo> users:Vector<User> =
photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;
upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
upload.fileCdnRedirect#1508485a dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes = upload.File;
upload.fileCdnRedirect#ea52fe5a dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes cdn_file_hashes:Vector<CdnFileHash> = upload.File;
dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true id:int ip_address:string port:int = DcOption;
dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int = DcOption;
config#cb601684 flags:# phonecalls_enabled:flags.1?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int rating_e_decay:int stickers_recent_limit:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string disabled_features:Vector<DisabledFeature> = Config;
config#7feec888 flags:# phonecalls_enabled:flags.1?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int rating_e_decay:int stickers_recent_limit:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string suggested_lang_code:flags.2?string lang_pack_version:flags.2?int disabled_features:Vector<DisabledFeature> = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
@@ -644,19 +649,16 @@ channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:
channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant;
channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant;
channelParticipantModerator#91057fef user_id:int inviter_id:int date:int = ChannelParticipant;
channelParticipantEditor#98192d61 user_id:int inviter_id:int date:int = ChannelParticipant;
channelParticipantKicked#8cc5e69a user_id:int kicked_by:int date:int = ChannelParticipant;
channelParticipantCreator#e3e2e1f9 user_id:int = ChannelParticipant;
channelParticipantAdmin#a82fa898 flags:# can_edit:flags.0?true user_id:int inviter_id:int promoted_by:int date:int admin_rights:ChannelAdminRights = ChannelParticipant;
channelParticipantBanned#222c1886 flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChannelBannedRights = ChannelParticipant;
channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter;
channelParticipantsAdmins#b4608969 = ChannelParticipantsFilter;
channelParticipantsKicked#3c37bb7a = ChannelParticipantsFilter;
channelParticipantsKicked#a3b54985 q:string = ChannelParticipantsFilter;
channelParticipantsBots#b0d1865b = ChannelParticipantsFilter;
channelRoleEmpty#b285a0c6 = ChannelParticipantRole;
channelRoleModerator#9618d975 = ChannelParticipantRole;
channelRoleEditor#820bfe8c = ChannelParticipantRole;
channelParticipantsBanned#1427a5e1 q:string = ChannelParticipantsFilter;
channelParticipantsSearch#656ac4b q:string = ChannelParticipantsFilter;
channels.channelParticipants#f56ee2a8 count:int participants:Vector<ChannelParticipant> users:Vector<User> = channels.ChannelParticipants;
@@ -697,7 +699,7 @@ messages.botResults#ccd3563d flags:# gallery:flags.0?true query_id:long next_off
exportedMessageLink#1f486803 link:string = ExportedMessageLink;
messageFwdHeader#c786ddcb flags:# from_id:flags.0?int date:int channel_id:flags.1?int channel_post:flags.2?int = MessageFwdHeader;
messageFwdHeader#fadff4ac flags:# from_id:flags.0?int date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string = MessageFwdHeader;
auth.codeTypeSms#72a3158c = auth.CodeType;
auth.codeTypeCall#741cd3e3 = auth.CodeType;
@@ -725,6 +727,7 @@ topPeerCategoryBotsInline#148677e2 = TopPeerCategory;
topPeerCategoryCorrespondents#637b7ed = TopPeerCategory;
topPeerCategoryGroups#bd17a14a = TopPeerCategory;
topPeerCategoryChannels#161d9628 = TopPeerCategory;
topPeerCategoryPhoneCalls#1e76a78c = TopPeerCategory;
topPeerCategoryPeers#fb834291 category:TopPeerCategory count:int peers:Vector<TopPeer> = TopPeerCategoryPeers;
@@ -795,9 +798,10 @@ pageBlockEmbedPost#292c7be9 url:string webpage_id:long author_photo_id:long auth
pageBlockCollage#8b31c4f items:Vector<PageBlock> caption:RichText = PageBlock;
pageBlockSlideshow#130c8963 items:Vector<PageBlock> caption:RichText = PageBlock;
pageBlockChannel#ef1751b5 channel:Chat = PageBlock;
pageBlockAudio#31b81a7f audio_id:long caption:RichText = PageBlock;
pagePart#8dee6c44 blocks:Vector<PageBlock> photos:Vector<Photo> videos:Vector<Document> = Page;
pageFull#d7a19d69 blocks:Vector<PageBlock> photos:Vector<Photo> videos:Vector<Document> = Page;
pagePart#8e3f9ebe blocks:Vector<PageBlock> photos:Vector<Photo> documents:Vector<Document> = Page;
pageFull#556ec7aa blocks:Vector<PageBlock> photos:Vector<Photo> documents:Vector<Document> = Page;
phoneCallDiscardReasonMissed#85e42301 = PhoneCallDiscardReason;
phoneCallDiscardReasonDisconnect#e095c1a0 = PhoneCallDiscardReason;
@@ -844,6 +848,8 @@ account.tmpPassword#db64fd34 tmp_password:bytes valid_until:int = account.TmpPas
shippingOption#b6213cdf id:string title:string prices:Vector<LabeledPrice> = ShippingOption;
inputStickerSetItem#ffa0a496 flags:# document:InputDocument emoji:string mask_coords:flags.0?MaskCoords = InputStickerSetItem;
inputPhoneCall#1e36fded id:long access_hash:long = InputPhoneCall;
phoneCallEmpty#5366c915 id:long = PhoneCall;
@@ -866,11 +872,48 @@ cdnPublicKey#c982eaba dc_id:int public_key:string = CdnPublicKey;
cdnConfig#5725e40a public_keys:Vector<CdnPublicKey> = CdnConfig;
langPackString#cad181f6 key:string value:string = LangPackString;
langPackStringPluralized#6c47ac9f flags:# key:string zero_value:flags.0?string one_value:flags.1?string two_value:flags.2?string few_value:flags.3?string many_value:flags.4?string other_value:string = LangPackString;
langPackStringDeleted#2979eeb2 key:string = LangPackString;
langPackDifference#f385c1f6 lang_code:string from_version:int version:int strings:Vector<LangPackString> = LangPackDifference;
langPackLanguage#117698f1 name:string native_name:string lang_code:string = LangPackLanguage;
channelAdminRights#5d7ceba5 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 invite_link:flags.6?true pin_messages:flags.7?true add_admins:flags.9?true = ChannelAdminRights;
channelBannedRights#58cf4249 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 until_date:int = ChannelBannedRights;
channelAdminLogEventActionChangeTitle#e6dfb825 prev_value:string new_value:string = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAbout#55188a2e prev_value:string new_value:string = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeUsername#6a4afc38 prev_value:string new_value:string = ChannelAdminLogEventAction;
channelAdminLogEventActionChangePhoto#b82f55c3 prev_photo:ChatPhoto new_photo:ChatPhoto = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleInvites#1b7907ae new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionToggleSignatures#26ae0971 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionUpdatePinned#e9e82c18 message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionEditMessage#709b2405 prev_message:Message new_message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionDeleteMessage#42e047bb message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantJoin#183040d3 = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantLeave#f89777f2 = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantInvite#e31c34d8 participant:ChannelParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantToggleBan#e6d83d7e prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantToggleAdmin#d5676710 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults;
channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true = ChannelAdminLogEventsFilter;
popularContact#5ce14175 client_id:long importers:int = PopularContact;
cdnFileHash#77eec38f offset:int limit:int hash:bytes = CdnFileHash;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector<long> query:!X = X;
initConnection#69796de9 {X:Type} api_id:int device_model:string system_version:string app_version:string lang_code:string query:!X = X;
initConnection#c7481da6 {X:Type} api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string query:!X = X;
invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
@@ -935,13 +978,13 @@ contacts.exportCard#84e53737 = Vector<int>;
contacts.importCard#4fe196fe export_card:Vector<int> = User;
contacts.search#11f812d8 q:string limit:int = contacts.Found;
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers;
contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers;
contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool;
messages.getMessages#4222fa74 id:Vector<int> = messages.Messages;
messages.getDialogs#191ba9c5 flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int = messages.Dialogs;
messages.getHistory#afa92846 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.search#d4569248 flags:# peer:InputPeer q:string filter:MessagesFilter min_date:int max_date:int offset:int max_id:int limit:int = messages.Messages;
messages.search#f288a275 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset:int max_id:int limit:int = messages.Messages;
messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages;
messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true peer:InputPeer max_id:int = messages.AffectedHistory;
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
@@ -1023,6 +1066,8 @@ messages.reorderPinnedDialogs#959ff644 flags:# force:flags.0?true order:Vector<I
messages.getPinnedDialogs#e254d64e = messages.PeerDialogs;
messages.setBotShippingResults#e5f672fa flags:# query_id:long error:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = Bool;
messages.setBotPrecheckoutResults#9c2dd95 flags:# success:flags.1?true query_id:long error:flags.0?string = Bool;
messages.uploadMedia#519bc2b1 peer:InputPeer media:InputMedia = MessageMedia;
messages.sendScreenshotNotification#c97df020 peer:InputPeer reply_to_msg_id:int random_id:long = Updates;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@@ -1038,7 +1083,8 @@ upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload
upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile;
upload.reuploadCdnFile#2e7a2020 file_token:bytes request_token:bytes = Bool;
upload.reuploadCdnFile#1af91c09 file_token:bytes request_token:bytes = Vector<CdnFileHash>;
upload.getCdnFileHashes#f715c87b file_token:bytes offset:int = Vector<CdnFileHash>;
help.getConfig#c4f9186b = Config;
help.getNearestDc#1fb33026 = NearestDc;
@@ -1062,7 +1108,7 @@ channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats;
channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull;
channels.createChannel#f4893d7f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string = Updates;
channels.editAbout#13e27f1e channel:InputChannel about:string = Bool;
channels.editAdmin#eb7611d0 channel:InputChannel user_id:InputUser role:ChannelParticipantRole = Updates;
channels.editAdmin#20b88214 channel:InputChannel user_id:InputUser admin_rights:ChannelAdminRights = Updates;
channels.editTitle#566decd0 channel:InputChannel title:string = Updates;
channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates;
channels.checkUsername#10e6bd2c channel:InputChannel username:string = Bool;
@@ -1070,7 +1116,6 @@ channels.updateUsername#3514b3de channel:InputChannel username:string = Bool;
channels.joinChannel#24b524c5 channel:InputChannel = Updates;
channels.leaveChannel#f836aa95 channel:InputChannel = Updates;
channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> = Updates;
channels.kickFromChannel#a672de14 channel:InputChannel user_id:InputUser kicked:Bool = Updates;
channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite;
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.toggleInvites#49609307 channel:InputChannel enabled:Bool = Updates;
@@ -1078,6 +1123,8 @@ channels.exportMessageLink#c846d22d channel:InputChannel id:int = ExportedMessag
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates;
channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats;
channels.editBanned#bfd915cd channel:InputChannel user_id:InputUser banned_rights:ChannelBannedRights = Updates;
channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
@@ -1089,6 +1136,11 @@ payments.sendPaymentForm#2b8879b3 flags:# msg_id:int requested_info_id:flags.0?s
payments.getSavedInfo#227d824b = payments.SavedInfo;
payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool;
stickers.createStickerSet#9bd86e6a flags:# masks:flags.0?true user_id:InputUser title:string short_name:string stickers:Vector<InputStickerSetItem> = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;
stickers.changeStickerPosition#ffb6d4ca sticker:InputDocument position:int = messages.StickerSet;
stickers.addStickerToSet#8653febe stickerset:InputStickerSet sticker:InputStickerSetItem = messages.StickerSet;
phone.getCallConfig#55451fa9 = DataJSON;
phone.requestCall#5b95b3d4 user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
@@ -1098,4 +1150,9 @@ phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDisc
phone.setCallRating#1c536a34 peer:InputPhoneCall rating:int comment:string = Updates;
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
// LAYER 66
langpack.getLangPack#9ab5c58e lang_code:string = LangPackDifference;
langpack.getStrings#2e1ee318 lang_code:string keys:Vector<string> = Vector<LangPackString>;
langpack.getDifference#b2e4d7d from_version:int = LangPackDifference;
langpack.getLanguages#800fd57d = Vector<LangPackLanguage>;
// LAYER 70

View File

@@ -9,7 +9,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="1.1.7.0" />
Version="1.1.17.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View File

@@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,1,7,0
PRODUCTVERSION 1,1,7,0
FILEVERSION 1,1,17,0
PRODUCTVERSION 1,1,17,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -52,10 +52,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "1.1.7.0"
VALUE "FileVersion", "1.1.17.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2017"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.1.7.0"
VALUE "ProductVersion", "1.1.17.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,1,7,0
PRODUCTVERSION 1,1,7,0
FILEVERSION 1,1,17,0
PRODUCTVERSION 1,1,17,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -43,10 +43,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "1.1.7.0"
VALUE "FileVersion", "1.1.17.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2017"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "1.1.7.0"
VALUE "ProductVersion", "1.1.17.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -22,12 +22,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "data/data_drafts.h"
#include "observer_peer.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "application.h"
#include "mainwindow.h"
#include "messenger.h"
#include "mainwidget.h"
#include "historywidget.h"
#include "history/history_widget.h"
#include "storage/localstorage.h"
#include "auth_session.h"
#include "boxes/confirm_box.h"
@@ -44,11 +44,75 @@ constexpr auto kSmallDelayMs = 5;
} // namespace
ApiWrap::ApiWrap()
: _messageDataResolveDelayed([this] { resolveMessageDatas(); })
ApiWrap::ApiWrap(gsl::not_null<AuthSession*> session)
: _session(session)
, _messageDataResolveDelayed([this] { resolveMessageDatas(); })
, _webPagesTimer([this] { resolveWebPages(); })
, _draftsSaveTimer([this] { saveDraftsToCloud(); }) {
}
void ApiWrap::start() {
Window::Theme::Background()->start();
requestAppChangelogs();
}
void ApiWrap::requestAppChangelogs() {
auto oldAppVersion = Local::oldMapVersion();
if (oldAppVersion > 0 && oldAppVersion < AppVersion) {
_changelogSubscription = subscribe(_session->data().moreChatsLoaded(), [this, oldAppVersion] {
auto oldVersionString = qsl("%1.%2.%3").arg(oldAppVersion / 1000000).arg((oldAppVersion % 1000000) / 1000).arg(oldAppVersion % 1000);
request(MTPhelp_GetAppChangelog(MTP_string(oldVersionString))).done([this, oldAppVersion](const MTPUpdates &result) {
applyUpdates(result);
auto resultEmpty = true;
switch (result.type()) {
case mtpc_updateShortMessage:
case mtpc_updateShortChatMessage:
case mtpc_updateShort: resultEmpty = false; break;
case mtpc_updatesCombined: resultEmpty = result.c_updatesCombined().vupdates.v.isEmpty(); break;
case mtpc_updates: resultEmpty = result.c_updates().vupdates.v.isEmpty(); break;
case mtpc_updatesTooLong:
case mtpc_updateShortSentMessage: LOG(("API Error: Bad updates type in app changelog.")); break;
}
if (resultEmpty) {
addLocalChangelogs(oldAppVersion);
}
}).send();
unsubscribe(base::take(_changelogSubscription));
});
}
}
void ApiWrap::addLocalChangelogs(int oldAppVersion) {
auto addedSome = false;
auto addLocalChangelog = [this, &addedSome](const QString &text) {
auto textWithEntities = TextWithEntities { text };
TextUtilities::ParseEntities(textWithEntities, TextParseLinks);
App::wnd()->serviceNotification(textWithEntities, MTP_messageMediaEmpty(), unixtime());
addedSome = true;
};
if (cAlphaVersion() || cBetaVersion()) {
auto addLocalAlphaChangelog = [this, oldAppVersion, addLocalChangelog](int changeVersion, const char *changes) {
if (oldAppVersion < changeVersion) {
auto changeVersionString = QString::number(changeVersion / 1000000) + '.' + QString::number((changeVersion % 1000000) / 1000) + ((changeVersion % 1000) ? ('.' + QString::number(changeVersion % 1000)) : QString());
auto text = qsl("New in version %1:\n\n").arg(changeVersionString) + QString::fromUtf8(changes).trimmed();
addLocalChangelog(text);
}
};
addLocalAlphaChangelog(1001008, "\xE2\x80\x94 Toggle night mode in the main menu.\n");
addLocalAlphaChangelog(1001010, "\xE2\x80\x94 Filter added to channel and supergroup event log.\n\xE2\x80\x94 Search by username in privacy exceptions editor fixed.\n\xE2\x80\x94 Adding admins in channels fixed.");
addLocalAlphaChangelog(1001011, "\xE2\x80\x94 Send **bold** and __italic__ text in your messages (in addition to already supported `monospace` and ```multiline monospace```).\n\xE2\x80\x94 Search in channel and supergroup admin event log.\n\xE2\x80\x94 Ban members from right click menu in supergroup admin event log.");
addLocalAlphaChangelog(1001012, "\xE2\x80\x94 Click on forwarded messages bar to change the recipient chat in case you chose a wrong one first.\n\xE2\x80\x94 Quickly share posts from channels and media messages from bots.\n\xE2\x80\x94 Search in large supergroup members by name.\n\xE2\x80\x94 Search in channel members by name if you're a channel admin.\n\xE2\x80\x94 Copy links to messages in public supergroups.");
addLocalAlphaChangelog(1001014, "\xE2\x80\x94 Bug fixes and other minor improvements.");
}
if (!addedSome) {
auto text = lng_new_version_wrap(lt_version, str_const_toString(AppVersionStr), lt_changes, lang(lng_new_version_minor), lt_link, qsl("https://desktop.telegram.org/changelog")).trimmed();
addLocalChangelog(text);
}
}
void ApiWrap::applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId) {
App::main()->feedUpdates(updates, sentMessageRandomId);
}
void ApiWrap::requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback callback) {
@@ -114,10 +178,6 @@ void ApiWrap::resolveMessageDatas() {
}
}
void ApiWrap::updatesReceived(const MTPUpdates &updates) {
App::main()->sentUpdatesReceived(updates);
}
void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId requestId) {
switch (msgs.type()) {
case mtpc_messages_messages: {
@@ -204,9 +264,9 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
auto &vc = d.vchats.v;
auto badVersion = false;
if (peer->isChat()) {
badVersion = (!vc.isEmpty() && vc.at(0).type() == mtpc_chat && vc.at(0).c_chat().vversion.v < peer->asChat()->version);
badVersion = (!vc.isEmpty() && vc[0].type() == mtpc_chat && vc[0].c_chat().vversion.v < peer->asChat()->version);
} else if (peer->isChannel()) {
badVersion = (!vc.isEmpty() && vc.at(0).type() == mtpc_channel && vc.at(0).c_channel().vversion.v < peer->asChannel()->version);
badVersion = (!vc.isEmpty() && vc[0].type() == mtpc_channel && vc[0].c_channel().vversion.v < peer->asChannel()->version);
}
App::feedUsers(d.vusers);
@@ -239,6 +299,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
chat->photoId = 0;
}
chat->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString());
chat->fullUpdated();
notifySettingReceived(MTP_inputNotifyPeer(peer->input), f.vnotify_settings);
} else if (auto channel = peer->asChannel()) {
@@ -303,6 +364,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
channel->setAbout(qs(f.vabout));
channel->setMembersCount(f.has_participants_count() ? f.vparticipants_count.v : 0);
channel->setAdminsCount(f.has_admins_count() ? f.vadmins_count.v : 0);
channel->setRestrictedCount(f.has_banned_count() ? f.vbanned_count.v : 0);
channel->setKickedCount(f.has_kicked_count() ? f.vkicked_count.v : 0);
channel->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString());
if (auto h = App::historyLoaded(channel->id)) {
@@ -321,23 +383,23 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
}
channel->fullUpdated();
if (canViewAdmins != channel->canViewAdmins()) Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelCanViewAdmins);
if (canViewMembers != channel->canViewMembers()) Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelCanViewMembers);
if (canViewAdmins != channel->canViewAdmins()
|| canViewMembers != channel->canViewMembers()) Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelRightsChanged);
notifySettingReceived(MTP_inputNotifyPeer(peer->input), f.vnotify_settings);
}
if (req) {
QMap<PeerData*, mtpRequestId>::iterator i = _fullPeerRequests.find(peer);
auto i = _fullPeerRequests.find(peer);
if (i != _fullPeerRequests.cend() && i.value() == req) {
_fullPeerRequests.erase(i);
}
}
if (badVersion) {
if (peer->isChat()) {
peer->asChat()->version = vc.at(0).c_chat().vversion.v;
} else if (peer->isChannel()) {
peer->asChannel()->version = vc.at(0).c_channel().vversion.v;
if (auto chat = peer->asChat()) {
chat->version = vc[0].c_chat().vversion.v;
} else if (auto channel = peer->asChannel()) {
channel->version = vc[0].c_channel().vversion.v;
}
requestPeer(peer);
}
@@ -365,6 +427,7 @@ void ApiWrap::gotUserFull(UserData *user, const MTPUserFull &result, mtpRequestI
user->setCallsStatus(d.is_phone_calls_private() ? UserData::CallsStatus::Private : d.is_phone_calls_available() ? UserData::CallsStatus::Enabled : UserData::CallsStatus::Disabled);
user->setAbout(d.has_about() ? qs(d.vabout) : QString());
user->setCommonChatsCount(d.vcommon_chats_count.v);
user->fullUpdated();
if (req) {
auto i = _fullPeerRequests.find(user);
@@ -389,18 +452,18 @@ void ApiWrap::requestPeer(PeerData *peer) {
if (auto chats = Api::getChatsFromMessagesChats(result)) {
auto &v = chats->v;
bool badVersion = false;
if (peer->isChat()) {
badVersion = (!v.isEmpty() && v.at(0).type() == mtpc_chat && v.at(0).c_chat().vversion.v < peer->asChat()->version);
} else if (peer->isChannel()) {
badVersion = (!v.isEmpty() && v.at(0).type() == mtpc_channel && v.at(0).c_chat().vversion.v < peer->asChannel()->version);
if (auto chat = peer->asChat()) {
badVersion = (!v.isEmpty() && v[0].type() == mtpc_chat && v[0].c_chat().vversion.v < chat->version);
} else if (auto channel = peer->asChannel()) {
badVersion = (!v.isEmpty() && v[0].type() == mtpc_channel && v[0].c_channel().vversion.v < channel->version);
}
auto chat = App::feedChats(*chats);
if (chat == peer) {
if (badVersion) {
if (peer->isChat()) {
peer->asChat()->version = v.at(0).c_chat().vversion.v;
} else if (peer->isChannel()) {
peer->asChannel()->version = v.at(0).c_channel().vversion.v;
if (auto chat = peer->asChat()) {
chat->version = v[0].c_chat().vversion.v;
} else if (auto channel = peer->asChannel()) {
channel->version = v[0].c_channel().vversion.v;
}
requestPeer(peer);
}
@@ -464,7 +527,7 @@ void ApiWrap::requestLastParticipants(ChannelData *channel, bool fromStart) {
return;
}
auto needAdmins = channel->amEditor();
auto needAdmins = channel->canViewAdmins();
auto adminsOutdated = (channel->mgInfo->lastParticipantsStatus & MegagroupInfo::LastParticipantsAdminsOutdated) != 0;
if ((needAdmins && adminsOutdated) || channel->lastParticipantsCountOutdated()) {
fromStart = true;
@@ -539,19 +602,36 @@ void ApiWrap::lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelP
auto needBotsInfos = false;
auto botStatus = peer->mgInfo->botStatus;
auto keyboardBotFound = !h || !h->lastKeyboardFrom;
auto emptyAdminRights = MTP_channelAdminRights(MTP_flags(0));
auto emptyRestrictedRights = MTP_channelBannedRights(MTP_flags(0), MTP_int(0));
for_const (auto &participant, v) {
auto userId = UserId(0);
bool admin = false;
auto adminCanEdit = false;
auto adminRights = emptyAdminRights;
auto restrictedRights = emptyRestrictedRights;
switch (participant.type()) {
case mtpc_channelParticipant: userId = participant.c_channelParticipant().vuser_id.v; break;
case mtpc_channelParticipantSelf: userId = participant.c_channelParticipantSelf().vuser_id.v; break;
case mtpc_channelParticipantModerator: userId = participant.c_channelParticipantModerator().vuser_id.v; break;
case mtpc_channelParticipantEditor: userId = participant.c_channelParticipantEditor().vuser_id.v; admin = true; break;
case mtpc_channelParticipantKicked: userId = participant.c_channelParticipantKicked().vuser_id.v; break;
case mtpc_channelParticipantCreator: userId = participant.c_channelParticipantCreator().vuser_id.v; admin = true; break;
case mtpc_channelParticipantAdmin:
userId = participant.c_channelParticipantAdmin().vuser_id.v;
adminCanEdit = participant.c_channelParticipantAdmin().is_can_edit();
adminRights = participant.c_channelParticipantAdmin().vadmin_rights;
break;
case mtpc_channelParticipantBanned:
userId = participant.c_channelParticipantBanned().vuser_id.v;
restrictedRights = participant.c_channelParticipantBanned().vbanned_rights;
break;
case mtpc_channelParticipantCreator: userId = participant.c_channelParticipantCreator().vuser_id.v; break;
}
if (!userId) {
continue;
}
auto u = App::user(userId);
if (participant.type() == mtpc_channelParticipantCreator) {
peer->mgInfo->creator = u;
}
if (bots) {
if (u->botInfo) {
peer->mgInfo->bots.insert(u);
@@ -566,7 +646,11 @@ void ApiWrap::lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelP
} else {
if (peer->mgInfo->lastParticipants.indexOf(u) < 0) {
peer->mgInfo->lastParticipants.push_back(u);
if (admin) peer->mgInfo->lastAdmins.insert(u);
if (adminRights.c_channelAdminRights().vflags.v) {
peer->mgInfo->lastAdmins.insert(u, MegagroupInfo::Admin { adminRights, adminCanEdit });
} else if (restrictedRights.c_channelBannedRights().vflags.v != 0) {
peer->mgInfo->lastRestricted.insert(u, MegagroupInfo::Restricted { restrictedRights });
}
if (u->botInfo) {
peer->mgInfo->bots.insert(u);
if (peer->mgInfo->botStatus != 0 && peer->mgInfo->botStatus < 2) {
@@ -625,20 +709,17 @@ void ApiWrap::requestSelfParticipant(ChannelData *channel) {
} break;
case mtpc_channelParticipantCreator: {
auto &d = p.vparticipant.c_channelParticipantCreator();
channel->inviter = AuthSession::CurrentUserId();
channel->inviter = _session->userId();
channel->inviteDate = date(MTP_int(channel->date));
if (channel->mgInfo) {
channel->mgInfo->creator = App::self();
}
} break;
case mtpc_channelParticipantModerator: {
auto &d = p.vparticipant.c_channelParticipantModerator();
case mtpc_channelParticipantAdmin: {
auto &d = p.vparticipant.c_channelParticipantAdmin();
channel->inviter = d.vinviter_id.v;
channel->inviteDate = date(d.vdate);
} break;
case mtpc_channelParticipantEditor: {
auto &d = p.vparticipant.c_channelParticipantEditor();
channel->inviter = d.vinviter_id.v;
channel->inviteDate = date(d.vdate);
} break;
}
if (App::main()) App::main()->onSelfParticipantUpdated(channel);
@@ -652,44 +733,17 @@ void ApiWrap::requestSelfParticipant(ChannelData *channel) {
_selfParticipantRequests.insert(channel, requestId);
}
void ApiWrap::kickParticipant(PeerData *peer, UserData *user) {
void ApiWrap::kickParticipant(PeerData *peer, UserData *user, const MTPChannelBannedRights &currentRights) {
auto kick = KickRequest(peer, user);
if (_kickRequests.contains(kick)) return;
if (auto channel = peer->asChannel()) {
auto requestId = request(MTPchannels_KickFromChannel(channel->inputChannel, user->inputUser, MTP_bool(true))).done([this, peer, user](const MTPUpdates &result) {
App::main()->sentUpdatesReceived(result);
auto rights = ChannelData::KickedRestrictedRights();
auto requestId = request(MTPchannels_EditBanned(channel->inputChannel, user->inputUser, rights)).done([this, channel, user, currentRights, rights](const MTPUpdates &result) {
applyUpdates(result);
_kickRequests.remove(KickRequest(peer, user));
if (auto channel = peer->asMegagroup()) {
auto megagroupInfo = channel->mgInfo;
auto i = megagroupInfo->lastParticipants.indexOf(user);
if (i >= 0) {
megagroupInfo->lastParticipants.removeAt(i);
}
if (channel->membersCount() > 1) {
channel->setMembersCount(channel->membersCount() - 1);
} else {
megagroupInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
megagroupInfo->lastParticipantsCount = 0;
}
channel->setKickedCount(channel->kickedCount() + 1);
if (megagroupInfo->lastAdmins.contains(user)) {
megagroupInfo->lastAdmins.remove(user);
if (channel->adminsCount() > 1) {
channel->setAdminsCount(channel->adminsCount() - 1);
}
Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::AdminsChanged);
}
megagroupInfo->bots.remove(user);
if (megagroupInfo->bots.isEmpty() && megagroupInfo->botStatus > 0) {
megagroupInfo->botStatus = -1;
}
}
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
fullPeerUpdated().notify(peer);
_kickRequests.remove(KickRequest(channel, user));
channel->applyEditBanned(user, currentRights, rights);
}).fail([this, kick](const RPCError &error) {
_kickRequests.remove(kick);
}).send();
@@ -703,15 +757,15 @@ void ApiWrap::unblockParticipant(PeerData *peer, UserData *user) {
if (_kickRequests.contains(kick)) return;
if (auto channel = peer->asChannel()) {
auto requestId = request(MTPchannels_KickFromChannel(channel->inputChannel, user->inputUser, MTP_bool(false))).done([this, peer, user](const MTPUpdates &result) {
App::main()->sentUpdatesReceived(result);
auto requestId = request(MTPchannels_EditBanned(channel->inputChannel, user->inputUser, MTP_channelBannedRights(MTP_flags(0), MTP_int(0)))).done([this, peer, user](const MTPUpdates &result) {
applyUpdates(result);
_kickRequests.remove(KickRequest(peer, user));
if (auto channel = peer->asMegagroup()) {
if (channel->kickedCount() > 0) {
channel->setKickedCount(channel->kickedCount() - 1);
} else {
channel->updateFull(true);
channel->updateFullForced();
}
}
}).fail([this, kick](const RPCError &error) {
@@ -877,7 +931,7 @@ void ApiWrap::joinChannel(ChannelData *channel) {
} else if (!_channelAmInRequests.contains(channel)) {
auto requestId = request(MTPchannels_JoinChannel(channel->inputChannel)).done([this, channel](const MTPUpdates &result) {
_channelAmInRequests.remove(channel);
updatesReceived(result);
applyUpdates(result);
}).fail([this, channel](const RPCError &error) {
if (error.type() == qstr("CHANNELS_TOO_MUCH")) {
Ui::show(Box<InformBox>(lang(lng_join_channel_error)));
@@ -895,7 +949,7 @@ void ApiWrap::leaveChannel(ChannelData *channel) {
} else if (!_channelAmInRequests.contains(channel)) {
auto requestId = request(MTPchannels_LeaveChannel(channel->inputChannel)).done([this, channel](const MTPUpdates &result) {
_channelAmInRequests.remove(channel);
updatesReceived(result);
applyUpdates(result);
}).fail([this, channel](const RPCError &error) {
_channelAmInRequests.remove(channel);
}).send();
@@ -1128,7 +1182,7 @@ void ApiWrap::saveDraftsToCloud() {
if (!textWithTags.tags.isEmpty()) {
flags |= MTPmessages_SaveDraft::Flag::f_entities;
}
auto entities = linksToMTP(ConvertTextTagsToEntities(textWithTags.tags), true);
auto entities = TextUtilities::EntitiesToMTP(ConvertTextTagsToEntities(textWithTags.tags), TextUtilities::ConvertOption::SkipLocal);
cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(MTP_flags(flags), MTP_int(cloudDraft->msgId), history->peer->input, MTP_string(textWithTags.text), entities)).done([this, history](const MTPBool &result, mtpRequestId requestId) {
if (auto cloudDraft = history->cloudDraft()) {
@@ -1197,7 +1251,7 @@ PeerData *ApiWrap::notifySettingReceived(MTPInputNotifyPeer notifyPeer, const MT
}
} break;
}
AuthSession::Current().notifications().checkDelayed();
_session->notifications().checkDelayed();
return requestedPeer;
}
@@ -1391,7 +1445,7 @@ void ApiWrap::resolveWebPages() {
void ApiWrap::requestParticipantsCountDelayed(ChannelData *channel) {
_participantsCountRequestTimer.call(kReloadChannelMembersTimeout, [this, channel] {
channel->updateFull(true);
channel->updateFullForced();
});
}
@@ -1479,4 +1533,121 @@ void ApiWrap::stickersSaveOrder() {
}
}
void ApiWrap::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
switch (updates.type()) {
case mtpc_updateShortMessage: {
auto &d = updates.c_updateShortMessage();
auto flags = mtpCastFlags(d.vflags.v) | MTPDmessage::Flag::f_from_id;
App::histories().addNewMessage(MTP_message(MTP_flags(flags), d.vid, d.is_out() ? MTP_int(AuthSession::CurrentUserId()) : d.vuser_id, MTP_peerUser(d.is_out() ? d.vuser_id : MTP_int(AuthSession::CurrentUserId())), d.vfwd_from, d.vvia_bot_id, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty(), MTPnullMarkup, d.has_entities() ? d.ventities : MTPnullEntities, MTPint(), MTPint(), MTPstring()), NewMessageUnread);
} break;
case mtpc_updateShortChatMessage: {
auto &d = updates.c_updateShortChatMessage();
auto flags = mtpCastFlags(d.vflags.v) | MTPDmessage::Flag::f_from_id;
App::histories().addNewMessage(MTP_message(MTP_flags(flags), d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), d.vfwd_from, d.vvia_bot_id, d.vreply_to_msg_id, d.vdate, d.vmessage, MTP_messageMediaEmpty(), MTPnullMarkup, d.has_entities() ? d.ventities : MTPnullEntities, MTPint(), MTPint(), MTPstring()), NewMessageUnread);
} break;
case mtpc_updateShortSentMessage: {
auto &d = updates.c_updateShortSentMessage();
Q_UNUSED(d); // Sent message data was applied anyway.
} break;
default: Unexpected("Type in applyUpdatesNoPtsCheck()");
}
}
void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) {
switch (update.type()) {
case mtpc_updateNewMessage: {
auto &d = update.c_updateNewMessage();
auto needToAdd = true;
if (d.vmessage.type() == mtpc_message) { // index forwarded messages to links _overview
if (App::checkEntitiesAndViewsUpdate(d.vmessage.c_message())) { // already in blocks
LOG(("Skipping message, because it is already in blocks!"));
needToAdd = false;
}
}
if (needToAdd) {
App::histories().addNewMessage(d.vmessage, NewMessageUnread);
}
} break;
case mtpc_updateReadMessagesContents: {
auto &d = update.c_updateReadMessagesContents();
auto &v = d.vmessages.v;
for (auto i = 0, l = v.size(); i < l; ++i) {
if (auto item = App::histItemById(NoChannel, v.at(i).v)) {
if (item->isMediaUnread()) {
item->markMediaRead();
Ui::repaintHistoryItem(item);
if (item->out() && item->history()->peer->isUser()) {
auto when = App::main()->requestingDifference() ? 0 : unixtime();
item->history()->peer->asUser()->madeAction(when);
}
}
}
}
} break;
case mtpc_updateReadHistoryInbox: {
auto &d = update.c_updateReadHistoryInbox();
App::feedInboxRead(peerFromMTP(d.vpeer), d.vmax_id.v);
} break;
case mtpc_updateReadHistoryOutbox: {
auto &d = update.c_updateReadHistoryOutbox();
auto peerId = peerFromMTP(d.vpeer);
auto when = App::main()->requestingDifference() ? 0 : unixtime();
App::feedOutboxRead(peerId, d.vmax_id.v, when);
} break;
case mtpc_updateWebPage: {
auto &d = update.c_updateWebPage();
Q_UNUSED(d); // Web page was updated anyway.
} break;
case mtpc_updateDeleteMessages: {
auto &d = update.c_updateDeleteMessages();
App::feedWereDeleted(NoChannel, d.vmessages.v);
} break;
case mtpc_updateNewChannelMessage: {
auto &d = update.c_updateNewChannelMessage();
auto needToAdd = true;
if (d.vmessage.type() == mtpc_message) { // index forwarded messages to links _overview
if (App::checkEntitiesAndViewsUpdate(d.vmessage.c_message())) { // already in blocks
LOG(("Skipping message, because it is already in blocks!"));
needToAdd = false;
}
}
if (needToAdd) {
App::histories().addNewMessage(d.vmessage, NewMessageUnread);
}
} break;
case mtpc_updateEditChannelMessage: {
auto &d = update.c_updateEditChannelMessage();
App::updateEditedMessage(d.vmessage);
} break;
case mtpc_updateEditMessage: {
auto &d = update.c_updateEditMessage();
App::updateEditedMessage(d.vmessage);
} break;
case mtpc_updateChannelWebPage: {
auto &d = update.c_updateChannelWebPage();
Q_UNUSED(d); // Web page was updated anyway.
} break;
case mtpc_updateDeleteChannelMessages: {
auto &d = update.c_updateDeleteChannelMessages();
App::feedWereDeleted(d.vchannel_id.v, d.vmessages.v);
} break;
default: Unexpected("Type in applyUpdateNoPtsCheck()");
}
}
ApiWrap::~ApiWrap() = default;

View File

@@ -24,6 +24,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "core/single_timer.h"
#include "mtproto/sender.h"
class AuthSession;
namespace Api {
inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Chats &chats) {
@@ -36,9 +38,12 @@ inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Cha
} // namespace Api
class ApiWrap : private MTP::Sender {
class ApiWrap : private MTP::Sender, private base::Subscriber {
public:
ApiWrap();
ApiWrap(gsl::not_null<AuthSession*> session);
void start();
void applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId = 0);
using RequestMessageDataCallback = base::lambda<void(ChannelData*, MsgId)>;
void requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback callback);
@@ -54,7 +59,7 @@ public:
void processFullPeer(UserData *user, const MTPUserFull &result);
void requestSelfParticipant(ChannelData *channel);
void kickParticipant(PeerData *peer, UserData *user);
void kickParticipant(PeerData *peer, UserData *user, const MTPChannelBannedRights &currentRights);
void unblockParticipant(PeerData *peer, UserData *user);
void requestWebPageDelayed(WebPageData *page);
@@ -86,6 +91,9 @@ public:
bool isQuitPrevent();
void applyUpdatesNoPtsCheck(const MTPUpdates &updates);
void applyUpdateNoPtsCheck(const MTPUpdate &update);
~ApiWrap();
private:
@@ -96,6 +104,8 @@ private:
};
using MessageDataRequests = QMap<MsgId, MessageDataRequest>;
void requestAppChangelogs();
void addLocalChangelogs(int oldAppVersion);
void updatesReceived(const MTPUpdates &updates);
void checkQuitPreventFinished();
@@ -119,6 +129,9 @@ private:
void stickerSetDisenabled(mtpRequestId requestId);
void stickersSaveOrder();
gsl::not_null<AuthSession*> _session;
mtpRequestId _changelogSubscription = 0;
MessageDataRequests _messageDataRequests;
QMap<ChannelData*, MessageDataRequests> _channelMessageDataRequests;
SingleQueuedInvokation _messageDataResolveDelayed;

View File

@@ -29,7 +29,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_chat_helpers.h"
#include "styles/style_history.h"
#include "styles/style_boxes.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "data/data_abstract_structure.h"
#include "history/history_service_layout.h"
#include "history/history_location_manager.h"
@@ -111,15 +111,12 @@ namespace {
style::font monofont;
struct CornersPixmaps {
CornersPixmaps() {
memset(p, 0, sizeof(p));
}
QPixmap *p[4];
QPixmap p[4];
};
CornersPixmaps corners[RoundCornersCount];
QVector<CornersPixmaps> corners;
using CornersMap = QMap<uint32, CornersPixmaps>;
CornersMap cornersMap;
QImage *cornersMaskLarge[4] = { nullptr }, *cornersMaskSmall[4] = { nullptr };
QImage cornersMaskLarge[4], cornersMaskSmall[4];
using EmojiImagesMap = QMap<int, QPixmap>;
EmojiImagesMap MainEmojiMap;
@@ -459,11 +456,11 @@ namespace {
// apply first_name and last_name from minimal user only if we don't have
// local values for first name and last name already, otherwise skip
bool noLocalName = data->firstName.isEmpty() && data->lastName.isEmpty();
QString fname = (!minimal || noLocalName) ? (d.has_first_name() ? textOneLine(qs(d.vfirst_name)) : QString()) : data->firstName;
QString lname = (!minimal || noLocalName) ? (d.has_last_name() ? textOneLine(qs(d.vlast_name)) : QString()) : data->lastName;
QString fname = (!minimal || noLocalName) ? (d.has_first_name() ? TextUtilities::SingleLine(qs(d.vfirst_name)) : QString()) : data->firstName;
QString lname = (!minimal || noLocalName) ? (d.has_last_name() ? TextUtilities::SingleLine(qs(d.vlast_name)) : QString()) : data->lastName;
QString phone = minimal ? data->phone() : (d.has_phone() ? qs(d.vphone) : QString());
QString uname = minimal ? data->username : (d.has_username() ? textOneLine(qs(d.vusername)) : QString());
QString uname = minimal ? data->username : (d.has_username() ? TextUtilities::SingleLine(qs(d.vusername)) : QString());
bool phoneChanged = (data->phone() != phone);
if (phoneChanged) {
@@ -640,7 +637,7 @@ namespace {
cdata->flags = d.vflags.v;
cdata->count = d.vparticipants_count.v;
cdata->isForbidden = false;
cdata->setIsForbidden(false);
if (canEdit != cdata->canEdit()) {
update.flags |= UpdateFlag::ChatCanEdit;
}
@@ -659,13 +656,13 @@ namespace {
cdata->count = -1;
cdata->invalidateParticipants();
cdata->flags = 0;
cdata->isForbidden = true;
cdata->setIsForbidden(true);
if (canEdit != cdata->canEdit()) {
update.flags |= UpdateFlag::ChatCanEdit;
}
} break;
case mtpc_channel: {
auto &d(chat.c_channel());
auto &d = chat.c_channel();
auto peerId = peerFromChannel(d.vid.v);
minimal = d.is_min();
@@ -681,16 +678,24 @@ namespace {
auto cdata = data->asChannel();
auto wasInChannel = cdata->amIn();
auto canEditPhoto = cdata->canEditPhoto();
auto canViewAdmins = cdata->canViewAdmins();
auto canViewMembers = cdata->canViewMembers();
auto canAddMembers = cdata->canAddMembers();
auto wasEditor = cdata->amEditor();
if (minimal) {
auto mask = MTPDchannel::Flag::f_broadcast | MTPDchannel::Flag::f_verified | MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_democracy;
cdata->flags = (cdata->flags & ~mask) | (d.vflags.v & mask);
} else {
if (d.has_admin_rights()) {
cdata->setAdminRights(d.vadmin_rights);
} else if (cdata->hasAdminRights()) {
cdata->setAdminRights(MTP_channelAdminRights(MTP_flags(0)));
}
if (d.has_banned_rights()) {
cdata->setRestrictedRights(d.vbanned_rights);
} else if (cdata->hasRestrictedRights()) {
cdata->setRestrictedRights(MTP_channelBannedRights(MTP_flags(0), MTP_int(0)));
}
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
cdata->access = d.vaccess_hash.v;
cdata->date = d.vdate.v;
@@ -706,21 +711,16 @@ namespace {
}
cdata->flagsUpdated();
QString uname = d.has_username() ? textOneLine(qs(d.vusername)) : QString();
QString uname = d.has_username() ? TextUtilities::SingleLine(qs(d.vusername)) : QString();
cdata->setName(qs(d.vtitle), uname);
cdata->isForbidden = false;
cdata->setIsForbidden(false);
cdata->setPhoto(d.vphoto);
if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn;
if (canEditPhoto != cdata->canEditPhoto()) update.flags |= UpdateFlag::ChannelCanEditPhoto;
if (canViewAdmins != cdata->canViewAdmins()) update.flags |= UpdateFlag::ChannelCanViewAdmins;
if (canViewMembers != cdata->canViewMembers()) update.flags |= UpdateFlag::ChannelCanViewMembers;
if (canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelCanAddMembers;
if (wasEditor != cdata->amEditor()) {
cdata->selfAdminUpdated();
update.flags |= (UpdateFlag::ChannelAmEditor | UpdateFlag::AdminsChanged);
}
if (canViewAdmins != cdata->canViewAdmins()
|| canViewMembers != cdata->canViewMembers()
|| canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelRightsChanged;
} break;
case mtpc_channelForbidden: {
auto &d(chat.c_channelForbidden());
@@ -731,11 +731,9 @@ namespace {
auto cdata = data->asChannel();
auto wasInChannel = cdata->amIn();
auto canEditPhoto = cdata->canEditPhoto();
auto canViewAdmins = cdata->canViewAdmins();
auto canViewMembers = cdata->canViewMembers();
auto canAddMembers = cdata->canAddMembers();
auto wasEditor = cdata->amEditor();
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
@@ -743,23 +741,25 @@ namespace {
cdata->flags = (cdata->flags & ~mask) | (mtpCastFlags(d.vflags) & mask);
cdata->flagsUpdated();
if (cdata->hasAdminRights()) {
cdata->setAdminRights(MTP_channelAdminRights(MTP_flags(0)));
}
if (cdata->hasRestrictedRights()) {
cdata->setRestrictedRights(MTP_channelBannedRights(MTP_flags(0), MTP_int(0)));
}
cdata->setName(qs(d.vtitle), QString());
cdata->access = d.vaccess_hash.v;
cdata->setPhoto(MTP_chatPhotoEmpty());
cdata->date = 0;
cdata->setMembersCount(0);
cdata->isForbidden = true;
cdata->setIsForbidden(true);
if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn;
if (canEditPhoto != cdata->canEditPhoto()) update.flags |= UpdateFlag::ChannelCanEditPhoto;
if (canViewAdmins != cdata->canViewAdmins()) update.flags |= UpdateFlag::ChannelCanViewAdmins;
if (canViewMembers != cdata->canViewMembers()) update.flags |= UpdateFlag::ChannelCanViewMembers;
if (canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelCanAddMembers;
if (wasEditor != cdata->amEditor()) {
cdata->selfAdminUpdated();
update.flags |= (UpdateFlag::ChannelAmEditor | UpdateFlag::AdminsChanged);
}
if (canViewAdmins != cdata->canViewAdmins()
|| canViewMembers != cdata->canViewMembers()
|| canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelRightsChanged;
} break;
}
if (!data) {
@@ -812,10 +812,10 @@ namespace {
auto &v = d.vparticipants.v;
chat->count = v.size();
int32 pversion = chat->participants.isEmpty() ? 1 : (chat->participants.begin().value() + 1);
chat->invitedByMe = ChatData::InvitedByMe();
chat->admins = ChatData::Admins();
chat->invitedByMe.clear();
chat->admins.clear();
chat->flags &= ~MTPDchat::Flag::f_admin;
for (QVector<MTPChatParticipant>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
for (auto i = v.cbegin(), e = v.cend(); i != e; ++i) {
int32 uid = 0, inviter = 0;
switch (i->type()) {
case mtpc_chatParticipantCreator: {
@@ -857,7 +857,7 @@ namespace {
History *h = App::historyLoaded(chat->id);
bool found = !h || !h->lastKeyboardFrom;
int32 botStatus = -1;
for (ChatData::Participants::iterator i = chat->participants.begin(), e = chat->participants.end(); i != e;) {
for (auto i = chat->participants.begin(), e = chat->participants.end(); i != e;) {
if (i.value() < pversion) {
i = chat->participants.erase(i);
} else {
@@ -963,7 +963,7 @@ namespace {
chat->count--;
}
} else {
ChatData::Participants::iterator i = chat->participants.find(user);
auto i = chat->participants.find(user);
if (i != chat->participants.end()) {
chat->participants.erase(i);
chat->count--;
@@ -980,7 +980,7 @@ namespace {
}
if (chat->botStatus > 0 && user->botInfo) {
int32 botStatus = -1;
for (ChatData::Participants::const_iterator j = chat->participants.cbegin(), e = chat->participants.cend(); j != e; ++j) {
for (auto j = chat->participants.cbegin(), e = chat->participants.cend(); j != e; ++j) {
if (j.key()->botInfo) {
if (true || botStatus > 0/* || !j.key()->botInfo->readsAllHistory*/) {
botStatus = 2;
@@ -1093,7 +1093,7 @@ namespace {
}
if (auto existing = App::histItemById(peerToChannel(peerId), m.vid.v)) {
auto text = qs(m.vmessage);
auto entities = m.has_entities() ? entitiesFromMTP(m.ventities.v) : EntitiesInText();
auto entities = m.has_entities() ? TextUtilities::EntitiesFromMTP(m.ventities.v) : EntitiesInText();
existing->setText({ text, entities });
existing->updateMedia(m.has_media() ? (&m.vmedia) : nullptr);
existing->updateReplyMarkup(m.has_reply_markup() ? (&m.vreply_markup) : nullptr);
@@ -1331,7 +1331,7 @@ namespace {
bool showPhone = !isServiceUser(user->id) && !user->isSelf() && !user->contact;
bool showPhoneChanged = !isServiceUser(user->id) && !user->isSelf() && ((showPhone && !wasShowPhone) || (!showPhone && wasShowPhone));
if (showPhoneChanged) {
user->setName(textOneLine(user->firstName), textOneLine(user->lastName), showPhone ? App::formatPhone(user->phone()) : QString(), textOneLine(user->username));
user->setName(TextUtilities::SingleLine(user->firstName), TextUtilities::SingleLine(user->lastName), showPhone ? App::formatPhone(user->phone()) : QString(), TextUtilities::SingleLine(user->username));
}
markPeerUpdated(user);
}
@@ -1494,11 +1494,18 @@ namespace {
}
WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert) {
return App::webPageSet(webpage.vid.v, convert, webpage.has_type() ? qs(webpage.vtype) : qsl("article"), qs(webpage.vurl), qs(webpage.vdisplay_url), webpage.has_site_name() ? qs(webpage.vsite_name) : QString(), webpage.has_title() ? qs(webpage.vtitle) : QString(), webpage.has_description() ? qs(webpage.vdescription) : QString(), webpage.has_photo() ? App::feedPhoto(webpage.vphoto) : 0, webpage.has_document() ? App::feedDocument(webpage.vdocument) : 0, webpage.has_duration() ? webpage.vduration.v : 0, webpage.has_author() ? qs(webpage.vauthor) : QString(), 0);
auto description = TextWithEntities { webpage.has_description() ? TextUtilities::Clean(qs(webpage.vdescription)) : QString() };
auto siteName = webpage.has_site_name() ? qs(webpage.vsite_name) : QString();
auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText;
if (siteName == qstr("Twitter") || siteName == qstr("Instagram")) {
parseFlags |= TextParseHashtags | TextParseMentions;
}
TextUtilities::ParseEntities(description, parseFlags);
return App::webPageSet(webpage.vid.v, convert, webpage.has_type() ? qs(webpage.vtype) : qsl("article"), qs(webpage.vurl), qs(webpage.vdisplay_url), siteName, webpage.has_title() ? qs(webpage.vtitle) : QString(), description, webpage.has_photo() ? App::feedPhoto(webpage.vphoto) : nullptr, webpage.has_document() ? App::feedDocument(webpage.vdocument) : nullptr, webpage.has_duration() ? webpage.vduration.v : 0, webpage.has_author() ? qs(webpage.vauthor) : QString(), 0);
}
WebPageData *feedWebPage(const MTPDwebPagePending &webpage, WebPageData *convert) {
return App::webPageSet(webpage.vid.v, convert, QString(), QString(), QString(), QString(), QString(), QString(), 0, 0, 0, QString(), webpage.vdate.v);
return App::webPageSet(webpage.vid.v, convert, QString(), QString(), QString(), QString(), QString(), TextWithEntities(), nullptr, nullptr, 0, QString(), webpage.vdate.v);
}
WebPageData *feedWebPage(const MTPWebPage &webpage) {
@@ -1515,6 +1522,10 @@ namespace {
return nullptr;
}
WebPageData *feedWebPage(WebPageId webPageId, const QString &siteName, const TextWithEntities &content) {
return App::webPageSet(webPageId, nullptr, qsl("article"), QString(), QString(), siteName, QString(), content, nullptr, nullptr, 0, QString(), 0);
}
GameData *feedGame(const MTPDgame &game, GameData *convert) {
return App::gameSet(game.vid.v, convert, game.vaccess_hash.v, qs(game.vshort_name), qs(game.vtitle), qs(game.vdescription), App::feedPhoto(game.vphoto), game.has_document() ? App::feedDocument(game.vdocument) : nullptr);
}
@@ -1775,7 +1786,7 @@ namespace {
return i.value();
}
WebPageData *webPageSet(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, DocumentData *document, int32 duration, const QString &author, int32 pendingTill) {
WebPageData *webPageSet(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const TextWithEntities &description, PhotoData *photo, DocumentData *document, int32 duration, const QString &author, int32 pendingTill) {
if (convert) {
if (convert->id != webPage) {
auto i = webPagesData.find(convert->id);
@@ -1786,15 +1797,15 @@ namespace {
}
if ((convert->url.isEmpty() && !url.isEmpty()) || (convert->pendingTill && convert->pendingTill != pendingTill && pendingTill >= -1)) {
convert->type = toWebPageType(type);
convert->url = textClean(url);
convert->displayUrl = textClean(displayUrl);
convert->siteName = textClean(siteName);
convert->title = textOneLine(textClean(title));
convert->description = textClean(description);
convert->url = TextUtilities::Clean(url);
convert->displayUrl = TextUtilities::Clean(displayUrl);
convert->siteName = TextUtilities::Clean(siteName);
convert->title = TextUtilities::SingleLine(title);
convert->description = description;
convert->photo = photo;
convert->document = document;
convert->duration = duration;
convert->author = textClean(author);
convert->author = TextUtilities::Clean(author);
if (convert->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(convert);
convert->pendingTill = pendingTill;
if (App::main()) App::main()->webPageUpdated(convert);
@@ -1817,15 +1828,15 @@ namespace {
if (result != convert) {
if ((result->url.isEmpty() && !url.isEmpty()) || (result->pendingTill && result->pendingTill != pendingTill && pendingTill >= -1)) {
result->type = toWebPageType(type);
result->url = textClean(url);
result->displayUrl = textClean(displayUrl);
result->siteName = textClean(siteName);
result->title = textOneLine(textClean(title));
result->description = textClean(description);
result->url = TextUtilities::Clean(url);
result->displayUrl = TextUtilities::Clean(displayUrl);
result->siteName = TextUtilities::Clean(siteName);
result->title = TextUtilities::SingleLine(title);
result->description = description;
result->photo = photo;
result->document = document;
result->duration = duration;
result->author = textClean(author);
result->author = TextUtilities::Clean(author);
if (result->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(result);
result->pendingTill = pendingTill;
if (App::main()) App::main()->webPageUpdated(result);
@@ -1855,9 +1866,9 @@ namespace {
}
if (!convert->accessHash && accessHash) {
convert->accessHash = accessHash;
convert->shortName = textClean(shortName);
convert->title = textOneLine(textClean(title));
convert->description = textClean(description);
convert->shortName = TextUtilities::Clean(shortName);
convert->title = TextUtilities::SingleLine(title);
convert->description = TextUtilities::Clean(description);
convert->photo = photo;
convert->document = document;
if (App::main()) App::main()->gameUpdated(convert);
@@ -1877,9 +1888,9 @@ namespace {
if (result != convert) {
if (!result->accessHash && accessHash) {
result->accessHash = accessHash;
result->shortName = textClean(shortName);
result->title = textOneLine(textClean(title));
result->description = textClean(description);
result->shortName = TextUtilities::Clean(shortName);
result->title = TextUtilities::SingleLine(title);
result->description = TextUtilities::Clean(description);
result->photo = photo;
result->document = document;
if (App::main()) App::main()->gameUpdated(result);
@@ -2160,6 +2171,7 @@ namespace {
}
void prepareCorners(RoundCorners index, int32 radius, const QBrush &brush, const style::color *shadow = nullptr, QImage *cors = nullptr) {
Expects(::corners.size() > index);
int32 r = radius * cIntRetinaFactor(), s = st::msgShadow * cIntRetinaFactor();
QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied), localCors[4];
{
@@ -2184,8 +2196,8 @@ namespace {
cors[3] = rect.copy(r * 2, r * 2, r, r + (shadow ? s : 0));
if (index != SmallMaskCorners && index != LargeMaskCorners) {
for (int i = 0; i < 4; ++i) {
::corners[index].p[i] = new QPixmap(pixmapFromImageInPlace(std::move(cors[i])));
::corners[index].p[i]->setDevicePixelRatio(cRetinaFactor());
::corners[index].p[i] = pixmapFromImageInPlace(std::move(cors[i]));
::corners[index].p[i].setDevicePixelRatio(cRetinaFactor());
}
}
}
@@ -2207,18 +2219,21 @@ namespace {
return MsgRadius;
}
void createCorners() {
void createMaskCorners() {
QImage mask[4];
prepareCorners(LargeMaskCorners, msgRadius(), QColor(255, 255, 255), nullptr, mask);
for (int i = 0; i < 4; ++i) {
::cornersMaskLarge[i] = new QImage(mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied));
::cornersMaskLarge[i]->setDevicePixelRatio(cRetinaFactor());
}
prepareCorners(SmallMaskCorners, st::buttonRadius, QColor(255, 255, 255), nullptr, mask);
for (int i = 0; i < 4; ++i) {
::cornersMaskSmall[i] = new QImage(mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied));
::cornersMaskSmall[i]->setDevicePixelRatio(cRetinaFactor());
::cornersMaskSmall[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied);
::cornersMaskSmall[i].setDevicePixelRatio(cRetinaFactor());
}
prepareCorners(LargeMaskCorners, msgRadius(), QColor(255, 255, 255), nullptr, mask);
for (int i = 0; i < 4; ++i) {
::cornersMaskLarge[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied);
::cornersMaskLarge[i].setDevicePixelRatio(cRetinaFactor());
}
}
void createPaletteCorners() {
prepareCorners(MenuCorners, st::buttonRadius, st::menuBg);
prepareCorners(BoxCorners, st::boxRadius, st::boxBg);
prepareCorners(BotKbOverCorners, st::dateRadius, st::msgBotKbOverBgAdd);
@@ -2235,8 +2250,6 @@ namespace {
prepareCorners(EmojiHoverCorners, st::buttonRadius, st::emojiPanHover);
prepareCorners(StickerHoverCorners, st::buttonRadius, st::emojiPanHover);
prepareCorners(BotKeyboardCorners, st::buttonRadius, st::botKbBg);
prepareCorners(BotKeyboardOverCorners, st::buttonRadius, st::botKbOverBg);
prepareCorners(BotKeyboardDownCorners, st::buttonRadius, st::botKbDownBg);
prepareCorners(PhotoSelectOverlayCorners, st::buttonRadius, st::overviewPhotoSelectOverlay);
prepareCorners(Doc1Corners, st::buttonRadius, st::msgFile1Bg);
@@ -2250,19 +2263,14 @@ namespace {
prepareCorners(MessageOutSelectedCorners, msgRadius(), st::msgOutBgSelected, &st::msgOutShadowSelected);
}
void createCorners() {
::corners.resize(RoundCornersCount);
createMaskCorners();
createPaletteCorners();
}
void clearCorners() {
for (int j = 0; j < 4; ++j) {
for (int i = 0; i < RoundCornersCount; ++i) {
delete ::corners[i].p[j]; ::corners[i].p[j] = nullptr;
}
delete ::cornersMaskSmall[j]; ::cornersMaskSmall[j] = nullptr;
delete ::cornersMaskLarge[j]; ::cornersMaskLarge[j] = nullptr;
}
for (auto i = ::cornersMap.cbegin(), e = ::cornersMap.cend(); i != e; ++i) {
for (int j = 0; j < 4; ++j) {
delete i->p[j];
}
}
::corners.clear();
::cornersMap.clear();
}
@@ -2291,18 +2299,13 @@ namespace {
using Update = Window::Theme::BackgroundUpdate;
static auto subscription = Window::Theme::Background()->add_subscription([](const Update &update) {
if (update.paletteChanged()) {
clearCorners();
createCorners();
createPaletteCorners();
if (App::main()) {
App::main()->updateScrollColors();
}
HistoryLayout::serviceColorsUpdated();
} else if (update.type == Update::Type::New) {
for (int i = 0; i < 4; ++i) {
delete ::corners[StickerCorners].p[i]; ::corners[StickerCorners].p[i] = nullptr;
delete ::corners[StickerSelectedCorners].p[i]; ::corners[StickerSelectedCorners].p[i] = nullptr;
}
prepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg);
prepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceBgSelected);
@@ -2766,7 +2769,7 @@ namespace {
roundRect(p, rect, st::msgInBg, MessageInCorners, nullptr, parts);
}
QImage **cornersMask(ImageRoundRadius radius) {
QImage *cornersMask(ImageRoundRadius radius) {
switch (radius) {
case ImageRoundRadius::Large: return ::cornersMaskLarge;
case ImageRoundRadius::Small:
@@ -2776,8 +2779,8 @@ namespace {
}
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, const CornersPixmaps &corner, const style::color *shadow, RectParts parts) {
auto cornerWidth = corner.p[0]->width() / cIntRetinaFactor();
auto cornerHeight = corner.p[0]->height() / cIntRetinaFactor();
auto cornerWidth = corner.p[0].width() / cIntRetinaFactor();
auto cornerHeight = corner.p[0].height() / cIntRetinaFactor();
if (w < 2 * cornerWidth || h < 2 * cornerHeight) return;
if (w > 2 * cornerWidth) {
if (parts & RectPart::Top) {
@@ -2806,16 +2809,16 @@ namespace {
}
}
if (parts & RectPart::TopLeft) {
p.drawPixmap(x, y, *corner.p[0]);
p.drawPixmap(x, y, corner.p[0]);
}
if (parts & RectPart::TopRight) {
p.drawPixmap(x + w - cornerWidth, y, *corner.p[1]);
p.drawPixmap(x + w - cornerWidth, y, corner.p[1]);
}
if (parts & RectPart::BottomLeft) {
p.drawPixmap(x, y + h - cornerHeight, *corner.p[2]);
p.drawPixmap(x, y + h - cornerHeight, corner.p[2]);
}
if (parts & RectPart::BottomRight) {
p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight, *corner.p[3]);
p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight, corner.p[3]);
}
}
@@ -2825,18 +2828,18 @@ namespace {
void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color shadow, RoundCorners index, RectParts parts) {
auto &corner = ::corners[index];
auto cornerWidth = corner.p[0]->width() / cIntRetinaFactor();
auto cornerHeight = corner.p[0]->height() / cIntRetinaFactor();
auto cornerWidth = corner.p[0].width() / cIntRetinaFactor();
auto cornerHeight = corner.p[0].height() / cIntRetinaFactor();
if (parts & RectPart::Bottom) {
p.fillRect(x + cornerWidth, y + h, w - 2 * cornerWidth, st::msgShadow, shadow);
}
if (parts & RectPart::BottomLeft) {
p.fillRect(x, y + h - cornerHeight, cornerWidth, st::msgShadow, shadow);
p.drawPixmap(x, y + h - cornerHeight + st::msgShadow, *corner.p[2]);
p.drawPixmap(x, y + h - cornerHeight + st::msgShadow, corner.p[2]);
}
if (parts & RectPart::BottomRight) {
p.fillRect(x + w - cornerWidth, y + h - cornerHeight, cornerWidth, st::msgShadow, shadow);
p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight + st::msgShadow, *corner.p[3]);
p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight + st::msgShadow, corner.p[3]);
}
}
@@ -2853,8 +2856,8 @@ namespace {
CornersPixmaps pixmaps;
for (int j = 0; j < 4; ++j) {
pixmaps.p[j] = new QPixmap(pixmapFromImageInPlace(std::move(images[j])));
pixmaps.p[j]->setDevicePixelRatio(cRetinaFactor());
pixmaps.p[j] = pixmapFromImageInPlace(std::move(images[j]));
pixmaps.p[j].setDevicePixelRatio(cRetinaFactor());
}
i = cornersMap.insert(colorKey, pixmaps);
}

View File

@@ -21,10 +21,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once
#include "core/basic_types.h"
#include "history.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_media.h"
#include "history/history_message.h"
#include "layout.h"
class Messenger;
@@ -104,6 +102,7 @@ namespace App {
WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert = nullptr);
WebPageData *feedWebPage(const MTPDwebPagePending &webpage, WebPageData *convert = nullptr);
WebPageData *feedWebPage(const MTPWebPage &webpage);
WebPageData *feedWebPage(WebPageId webPageId, const QString &siteName, const TextWithEntities &content);
GameData *feedGame(const MTPDgame &game, GameData *convert = nullptr);
PeerData *peer(const PeerId &id, PeerData::LoadedStatus restriction = PeerData::NotLoaded);
@@ -156,7 +155,7 @@ namespace App {
DocumentData *document(const DocumentId &document);
DocumentData *documentSet(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 version, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size, const StorageImageLocation &thumbLocation);
WebPageData *webPage(const WebPageId &webPage);
WebPageData *webPageSet(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill);
WebPageData *webPageSet(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const TextWithEntities &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill);
GameData *game(const GameId &game);
GameData *gameSet(const GameId &game, GameData *convert, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc);
LocationData *location(const LocationCoords &coords);
@@ -284,7 +283,7 @@ namespace App {
void complexOverlayRect(Painter &p, QRect rect, ImageRoundRadius radius, ImageRoundCorners corners);
void complexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, ImageRoundCorners corners);
QImage **cornersMask(ImageRoundRadius radius);
QImage *cornersMask(ImageRoundRadius radius);
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full);
inline void roundRect(Painter &p, const QRect &rect, style::color bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full) {
return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, shadow, parts);

View File

@@ -242,6 +242,7 @@ void Application::newInstanceConnected() {
}
void Application::readClients() {
// This method can be called before Messenger is constructed.
QString startUrl;
QStringList toSend;
for (LocalClients::iterator i = _localClients.begin(), e = _localClients.end(); i != e; ++i) {
@@ -286,8 +287,8 @@ void Application::readClients() {
if (!startUrl.isEmpty()) {
cSetStartUrl(startUrl);
}
if (auto main = App::main()) {
main->checkStartUrl();
if (auto messenger = Messenger::InstancePointer()) {
messenger->checkStartUrl();
}
}

View File

@@ -155,7 +155,7 @@ QString AuthSessionData::getSoundPath(const QString &key) const {
AuthSession::AuthSession(UserId userId)
: _userId(userId)
, _autoLockTimer([this] { checkAutoLock(); })
, _api(std::make_unique<ApiWrap>())
, _api(std::make_unique<ApiWrap>(this))
, _calls(std::make_unique<Calls::Instance>())
, _downloader(std::make_unique<Storage::Downloader>())
, _notifications(std::make_unique<Window::Notifications::System>(this)) {
@@ -167,6 +167,7 @@ AuthSession::AuthSession(UserId userId)
_shouldLockAt = 0;
notifications().updateAll();
});
_api->start();
}
bool AuthSession::Exists() {

View File

@@ -57,6 +57,22 @@ public:
base::Observable<void> &savedGifsUpdated() {
return _savedGifsUpdated;
}
base::Observable<gsl::not_null<History*>> &historyCleared() {
return _historyCleared;
}
base::Observable<gsl::not_null<const HistoryItem*>> &repaintLogEntry() {
return _repaintLogEntry;
}
base::Observable<void> &pendingHistoryResize() {
return _pendingHistoryResize;
}
struct ItemVisibilityQuery {
gsl::not_null<HistoryItem*> item;
gsl::not_null<bool*> isVisible;
};
base::Observable<ItemVisibilityQuery> &queryItemVisibility() {
return _queryItemVisibility;
}
void copyFrom(const AuthSessionData &other) {
_variables = other._variables;
@@ -131,6 +147,10 @@ private:
base::Variable<bool> _allChatsLoaded = { false };
base::Observable<void> _moreChatsLoaded;
base::Observable<void> _savedGifsUpdated;
base::Observable<gsl::not_null<History*>> _historyCleared;
base::Observable<gsl::not_null<const HistoryItem*>> _repaintLogEntry;
base::Observable<void> _pendingHistoryResize;
base::Observable<ItemVisibilityQuery> _queryItemVisibility;
Variables _variables;
TimeMs _lastTimeVideoPlayedAt = 0;

View File

@@ -427,9 +427,10 @@ void UpdateChecker::unpackUpdate() {
LOG(("Update Error: cant open file '%1' for writing").arg(tempDirPath + '/' + relativeName));
return fatalFail();
}
if (f.write(fileInnerData) != fileSize) {
auto writtenBytes = f.write(fileInnerData);
if (writtenBytes != fileSize) {
f.close();
LOG(("Update Error: cant write file '%1'").arg(tempDirPath + '/' + relativeName));
LOG(("Update Error: cant write file '%1', desiredSize: %2, write result: %3").arg(tempDirPath + '/' + relativeName).arg(fileSize).arg(writtenBytes));
return fatalFail();
}
f.close();

View File

@@ -335,12 +335,12 @@ public:
}
// Copy / move construct / assign from an arbitrary type.
template <typename Lambda, typename = decltype(std::declval<Lambda>()(std::declval<Args>()...))>
template <typename Lambda, typename = std::enable_if_t<std::is_convertible<decltype(std::declval<Lambda>()(std::declval<Args>()...)),Return>::value>>
lambda_once(Lambda other) {
data_.vtable = &lambda_internal::vtable_once<Lambda, Return, Args...>::instance;
lambda_internal::vtable_once<Lambda, Return, Args...>::construct_move_lambda_method(data_.storage, &other);
}
template <typename Lambda, typename = decltype(std::declval<Lambda>()(std::declval<Args>()...))>
template <typename Lambda, typename = std::enable_if_t<std::is_convertible<decltype(std::declval<Lambda>()(std::declval<Args>()...)),Return>::value>>
lambda_once &operator=(Lambda other) {
if (data_.vtable) {
data_.vtable->destruct(data_.storage);
@@ -414,11 +414,11 @@ public:
}
// Copy / move construct / assign from an arbitrary type.
template <typename Lambda, typename = decltype(std::declval<Lambda>()(std::declval<Args>()...))>
template <typename Lambda, typename = std::enable_if_t<std::is_convertible<decltype(std::declval<Lambda>()(std::declval<Args>()...)),Return>::value>>
lambda(Lambda other) : Parent(&lambda_internal::vtable<Lambda, Return, Args...>::instance, typename Parent::Private()) {
lambda_internal::vtable<Lambda, Return, Args...>::construct_move_lambda_method(this->data_.storage, &other);
}
template <typename Lambda, typename = decltype(std::declval<Lambda>()(std::declval<Args>()...))>
template <typename Lambda, typename = std::enable_if_t<std::is_convertible<decltype(std::declval<Lambda>()(std::declval<Args>()...)),Return>::value>>
lambda &operator=(Lambda other) {
if (this->data_.vtable) {
this->data_.vtable->destruct(this->data_.storage);

View File

@@ -229,10 +229,14 @@ private:
break;
}
} while (_current);
}
bool destroyMeIfEmpty() const {
if (empty()) {
_observable->_data.reset();
return true;
}
return false;
}
CommonObservable<EventType, Handler> *_observable = nullptr;
@@ -282,6 +286,9 @@ private:
this->notifyEnumerate([this, &event]() {
this->_current->handler(event);
});
if (this->destroyMeIfEmpty()) {
return;
}
}
_handling = false;
UnregisterActiveObservable(&this->_callHandlers);
@@ -329,6 +336,9 @@ private:
this->notifyEnumerate([this]() {
this->_current->handler();
});
if (this->destroyMeIfEmpty()) {
return;
}
}
_handling = false;
UnregisterActiveObservable(&this->_callHandlers);
@@ -357,9 +367,9 @@ class Observable : public internal::BaseObservable<EventType, Handler, base::typ
public:
Observable() = default;
Observable(const Observable &other) = delete;
Observable(Observable &&other) = default;
Observable(Observable &&other) = delete;
Observable &operator=(const Observable &other) = delete;
Observable &operator=(Observable &&other) = default;
Observable &operator=(Observable &&other) = delete;
};

View File

@@ -26,8 +26,8 @@ typedef void(*RuntimeComponentDestruct)(void *location);
typedef void(*RuntimeComponentMove)(void *location, void *waslocation);
struct RuntimeComponentWrapStruct {
// don't init any fields, because it is only created in
// global scope, so it will be filled by zeros from the start
// Don't init any fields, because it is only created in
// global scope, so it will be filled by zeros from the start.
RuntimeComponentWrapStruct() = default;
RuntimeComponentWrapStruct(std::size_t size, std::size_t align, RuntimeComponentConstruct construct, RuntimeComponentDestruct destruct, RuntimeComponentMove move)
: Size(size)
@@ -54,7 +54,8 @@ extern QAtomicInt RuntimeComponentIndexLast;
template <typename Type>
struct RuntimeComponent {
RuntimeComponent() {
static_assert(alignof(Type) <= alignof(SmallestSizeType), "Components should align to a pointer!");
// While there is no std::aligned_alloc().
static_assert(alignof(Type) <= alignof(std::max_align_t), "Components should align to std::max_align_t!");
}
RuntimeComponent(const RuntimeComponent &other) = delete;
RuntimeComponent &operator=(const RuntimeComponent &other) = delete;
@@ -62,17 +63,17 @@ struct RuntimeComponent {
RuntimeComponent &operator=(RuntimeComponent &&other) = default;
static int Index() {
static QAtomicInt _index(0);
if (int index = _index.loadAcquire()) {
static QAtomicInt MyIndex(0);
if (auto index = MyIndex.loadAcquire()) {
return index - 1;
}
while (true) {
int last = RuntimeComponentIndexLast.loadAcquire();
auto last = RuntimeComponentIndexLast.loadAcquire();
if (RuntimeComponentIndexLast.testAndSetOrdered(last, last + 1)) {
t_assert(last < 64);
if (_index.testAndSetOrdered(0, last + 1)) {
if (MyIndex.testAndSetOrdered(0, last + 1)) {
RuntimeComponentWraps[last] = RuntimeComponentWrapStruct(
CeilDivideMinimumOne<sizeof(Type), sizeof(SmallestSizeType)>::Result * sizeof(SmallestSizeType),
sizeof(Type),
alignof(Type),
Type::RuntimeComponentConstruct,
Type::RuntimeComponentDestruct,
@@ -81,15 +82,13 @@ struct RuntimeComponent {
break;
}
}
return _index.loadAcquire() - 1;
return MyIndex.loadAcquire() - 1;
}
static uint64 Bit() {
return (1ULL << Index());
}
protected:
using SmallestSizeType = void*;
static void RuntimeComponentConstruct(void *location, RuntimeComposer *composer) {
new (location) Type();
}
@@ -104,30 +103,32 @@ protected:
class RuntimeComposerMetadata {
public:
RuntimeComposerMetadata(uint64 mask) : size(0), last(64), _mask(mask) {
for (int i = 0; i < 64; ++i) {
uint64 m = (1ULL << i);
if (_mask & m) {
int s = RuntimeComponentWraps[i].Size;
if (s) {
RuntimeComposerMetadata(uint64 mask) : _mask(mask) {
for (int i = 0; i != 64; ++i) {
auto componentBit = (1ULL << i);
if (_mask & componentBit) {
auto componentSize = RuntimeComponentWraps[i].Size;
if (componentSize) {
auto componentAlign = RuntimeComponentWraps[i].Align;
if (auto badAlign = (size % componentAlign)) {
size += (componentAlign - badAlign);
}
offsets[i] = size;
size += s;
} else {
offsets[i] = -1;
size += componentSize;
accumulate_max(align, componentAlign);
}
} else if (_mask < m) {
} else if (_mask < componentBit) {
last = i;
for (; i < 64; ++i) {
offsets[i] = -1;
}
} else {
offsets[i] = -1;
break;
}
}
}
int size, last;
int offsets[64];
// Meta pointer in the start.
std::size_t size = sizeof(const RuntimeComposerMetadata*);
std::size_t align = alignof(const RuntimeComposerMetadata*);
std::size_t offsets[64] = { 0 };
int last = 64;
bool equals(uint64 mask) const {
return _mask == mask;
@@ -150,28 +151,28 @@ class RuntimeComposer {
public:
RuntimeComposer(uint64 mask = 0) : _data(zerodata()) {
if (mask) {
const RuntimeComposerMetadata *meta = GetRuntimeComposerMetadata(mask);
int size = sizeof(meta) + meta->size;
auto meta = GetRuntimeComposerMetadata(mask);
auto data = operator new(size);
auto data = operator new(meta->size);
t_assert(data != nullptr);
_data = data;
_meta() = meta;
for (int i = 0; i < meta->last; ++i) {
int offset = meta->offsets[i];
if (offset >= 0) {
auto offset = meta->offsets[i];
if (offset >= sizeof(_meta())) {
try {
auto constructAt = _dataptrunsafe(offset);
auto space = RuntimeComponentWraps[i].Size;
auto alignedAt = std::align(RuntimeComponentWraps[i].Align, space, constructAt, space);
auto alignedAt = constructAt;
std::align(RuntimeComponentWraps[i].Align, space, alignedAt, space);
t_assert(alignedAt == constructAt);
RuntimeComponentWraps[i].Construct(constructAt, this);
} catch (...) {
while (i > 0) {
--i;
offset = meta->offsets[--i];
if (offset >= 0) {
if (offset >= sizeof(_meta())) {
RuntimeComponentWraps[i].Destruct(_dataptrunsafe(offset));
}
}
@@ -187,8 +188,8 @@ public:
if (_data != zerodata()) {
auto meta = _meta();
for (int i = 0; i < meta->last; ++i) {
int offset = meta->offsets[i];
if (offset >= 0) {
auto offset = meta->offsets[i];
if (offset >= sizeof(_meta())) {
RuntimeComponentWraps[i].Destruct(_dataptrunsafe(offset));
}
}
@@ -198,7 +199,7 @@ public:
template <typename Type>
bool Has() const {
return (_meta()->offsets[Type::Index()] >= 0);
return (_meta()->offsets[Type::Index()] >= sizeof(_meta()));
}
template <typename Type>
@@ -218,8 +219,9 @@ protected:
if (_data != zerodata() && tmp._data != zerodata()) {
auto meta = _meta(), wasmeta = tmp._meta();
for (int i = 0; i < meta->last; ++i) {
int offset = meta->offsets[i], wasoffset = wasmeta->offsets[i];
if (offset >= 0 && wasoffset >= 0) {
auto offset = meta->offsets[i];
auto wasoffset = wasmeta->offsets[i];
if (offset >= sizeof(_meta()) && wasoffset >= sizeof(_meta())) {
RuntimeComponentWraps[i].Move(_dataptrunsafe(offset), tmp._dataptrunsafe(wasoffset));
}
}
@@ -240,15 +242,15 @@ private:
}
void *_dataptrunsafe(int skip) const {
return (char*)_data + sizeof(_meta()) + skip;
return (char*)_data + skip;
}
void *_dataptr(int skip) const {
return (skip >= 0) ? _dataptrunsafe(skip) : 0;
return (skip >= sizeof(_meta())) ? _dataptrunsafe(skip) : nullptr;
}
const RuntimeComposerMetadata *&_meta() const {
return *static_cast<const RuntimeComposerMetadata**>(_data);
}
void *_data;
void *_data = nullptr;
void swap(RuntimeComposer &other) {
std::swap(_data, other._data);

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/about_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "autoupdater.h"
@@ -39,9 +39,10 @@ AboutBox::AboutBox(QWidget *parent)
}
void AboutBox::prepare() {
setTitle(qsl("Telegram Desktop"));
constexpr auto test = std::is_convertible<const char*, QString>::value;
setTitle([] { return qsl("Telegram Desktop"); });
addButton(lang(lng_close), [this] { closeBox(); });
addButton(langFactory(lng_close), [this] { closeBox(); });
_text3->setRichText(lng_about_text_3(lt_faq_open, qsl("[a href=\"%1\"]").arg(telegramFaqLink()), lt_faq_close, qsl("[/a]")));
@@ -88,13 +89,11 @@ void AboutBox::keyPressEvent(QKeyEvent *e) {
}
QString telegramFaqLink() {
QString result = qsl("https://telegram.org/faq");
if (cLang() > languageDefault && cLang() < languageCount) {
const char *code = LanguageCodes[cLang()].c_str();
if (qstr("de") == code || qstr("es") == code || qstr("it") == code || qstr("ko") == code) {
result += qsl("/") + code;
} else if (qstr("pt_BR") == code) {
result += qsl("/br");
auto result = qsl("https://telegram.org/faq");
auto language = Lang::Current().id();
for (auto faqLanguage : { "de", "es", "it", "ko", "br" }) {
if (language.startsWith(QLatin1String(faqLanguage))) {
result.append('/').append(faqLanguage);
}
}
return result;

View File

@@ -22,7 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_boxes.h"
#include "storage/localstorage.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "ui/effects/widget_fade_wrap.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
@@ -33,12 +33,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
BoxLayerTitleShadow::BoxLayerTitleShadow(QWidget *parent) : Ui::PlainShadow(parent, st::boxLayerTitleShadow) {
}
QPointer<Ui::RoundButton> BoxContent::addButton(const QString &text, base::lambda<void()> clickCallback) {
return addButton(text, std::move(clickCallback), st::defaultBoxButton);
QPointer<Ui::RoundButton> BoxContent::addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback) {
return addButton(std::move(textFactory), std::move(clickCallback), st::defaultBoxButton);
}
QPointer<Ui::RoundButton> BoxContent::addLeftButton(const QString &text, base::lambda<void()> clickCallback) {
return getDelegate()->addLeftButton(text, std::move(clickCallback), st::defaultBoxButton);
QPointer<Ui::RoundButton> BoxContent::addLeftButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback) {
return getDelegate()->addLeftButton(std::move(textFactory), std::move(clickCallback), st::defaultBoxButton);
}
void BoxContent::setInner(object_ptr<TWidget> inner) {
@@ -58,9 +58,10 @@ void BoxContent::setInner(object_ptr<TWidget> inner, const style::ScrollArea &st
_topShadow.create(this, object_ptr<BoxLayerTitleShadow>(this));
_bottomShadow.create(this, object_ptr<BoxLayerTitleShadow>(this));
}
updateScrollAreaGeometry();
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(_scroll, SIGNAL(innerResized()), this, SLOT(onInnerResize()));
if (!_preparing) {
// We didn't set dimensions yet, this will be called from finishPrepare();
finishScrollCreate();
}
} else {
getDelegate()->setLayerType(false);
_scroll.destroyDelayed();
@@ -69,6 +70,21 @@ void BoxContent::setInner(object_ptr<TWidget> inner, const style::ScrollArea &st
}
}
void BoxContent::finishPrepare() {
_preparing = false;
if (_scroll) {
finishScrollCreate();
}
setInnerFocus();
}
void BoxContent::finishScrollCreate() {
Expects(_scroll != nullptr);
updateScrollAreaGeometry();
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
connect(_scroll, SIGNAL(innerResized()), this, SLOT(onInnerResize()));
}
void BoxContent::onScrollToY(int top, int bottom) {
if (_scroll) {
_scroll->scrollToY(top, bottom);
@@ -198,6 +214,7 @@ void BoxContent::paintEvent(QPaintEvent *e) {
AbstractBox::AbstractBox(QWidget *parent, Window::Controller *controller, object_ptr<BoxContent> content) : LayerWidget(parent)
, _controller(controller)
, _content(std::move(content)) {
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
_content->setParent(this);
_content->setDelegate(this);
}
@@ -256,17 +273,18 @@ void AbstractBox::parentResized() {
update();
}
void AbstractBox::setTitle(const QString &title) {
setTitle({ title, EntitiesInText() });
void AbstractBox::setTitle(base::lambda<TextWithEntities()> titleFactory) {
_titleFactory = std::move(titleFactory);
refreshTitle();
}
void AbstractBox::setTitle(const TextWithEntities &title) {
void AbstractBox::refreshTitle() {
auto wasTitle = hasTitle();
if (!title.text.isEmpty()) {
if (_titleFactory) {
if (!_title) {
_title.create(this, st::boxTitle);
}
_title->setMarkedText(title);
_title->setMarkedText(_titleFactory());
updateTitlePosition();
} else {
_title.destroy();
@@ -276,11 +294,22 @@ void AbstractBox::setTitle(const TextWithEntities &title) {
}
}
void AbstractBox::setAdditionalTitle(const QString &additional) {
_additionalTitle = additional;
void AbstractBox::setAdditionalTitle(base::lambda<QString()> additionalFactory) {
_additionalTitleFactory = std::move(additionalFactory);
refreshAdditionalTitle();
}
void AbstractBox::refreshAdditionalTitle() {
_additionalTitle = _additionalTitleFactory ? _additionalTitleFactory() : QString();
update();
}
void AbstractBox::refreshLang() {
refreshTitle();
refreshAdditionalTitle();
InvokeQueued(this, [this] { updateButtonsPositions(); });
}
bool AbstractBox::hasTitle() const {
return (_title != nullptr) || !_additionalTitle.isEmpty();
}
@@ -320,8 +349,8 @@ void AbstractBox::clearButtons() {
_leftButton.destroy();
}
QPointer<Ui::RoundButton> AbstractBox::addButton(const QString &text, base::lambda<void()> clickCallback, const style::RoundButton &st) {
_buttons.push_back(object_ptr<Ui::RoundButton>(this, text, st));
QPointer<Ui::RoundButton> AbstractBox::addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) {
_buttons.push_back(object_ptr<Ui::RoundButton>(this, std::move(textFactory), st));
auto result = QPointer<Ui::RoundButton>(_buttons.back());
result->setClickedCallback(std::move(clickCallback));
result->show();
@@ -329,8 +358,8 @@ QPointer<Ui::RoundButton> AbstractBox::addButton(const QString &text, base::lamb
return result;
}
QPointer<Ui::RoundButton> AbstractBox::addLeftButton(const QString &text, base::lambda<void()> clickCallback, const style::RoundButton &st) {
_leftButton = object_ptr<Ui::RoundButton>(this, text, st);
QPointer<Ui::RoundButton> AbstractBox::addLeftButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) {
_leftButton = object_ptr<Ui::RoundButton>(this, std::move(textFactory), st);
auto result = QPointer<Ui::RoundButton>(_leftButton);
result->setClickedCallback(std::move(clickCallback));
result->show();

View File

@@ -47,13 +47,12 @@ public:
virtual Window::Controller *controller() const = 0;
virtual void setLayerType(bool layerType) = 0;
virtual void setTitle(const QString &title) = 0;
virtual void setTitle(const TextWithEntities &title) = 0;
virtual void setAdditionalTitle(const QString &additional) = 0;
virtual void setTitle(base::lambda<TextWithEntities()> titleFactory) = 0;
virtual void setAdditionalTitle(base::lambda<QString()> additionalFactory) = 0;
virtual void clearButtons() = 0;
virtual QPointer<Ui::RoundButton> addButton(const QString &text, base::lambda<void()> clickCallback, const style::RoundButton &st) = 0;
virtual QPointer<Ui::RoundButton> addLeftButton(const QString &text, base::lambda<void()> clickCallback, const style::RoundButton &st) = 0;
virtual QPointer<Ui::RoundButton> addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) = 0;
virtual QPointer<Ui::RoundButton> addLeftButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) = 0;
virtual void updateButtonsPositions() = 0;
virtual void setDimensions(int newWidth, int maxHeight) = 0;
@@ -78,23 +77,27 @@ public:
getDelegate()->closeBox();
}
void setTitle(const QString &title) {
getDelegate()->setTitle(title);
void setTitle(base::lambda<QString()> titleFactory) {
if (titleFactory) {
getDelegate()->setTitle([titleFactory] { return TextWithEntities { titleFactory(), EntitiesInText() }; });
} else {
getDelegate()->setTitle(base::lambda<TextWithEntities()>());
}
}
void setTitle(const TextWithEntities &title) {
getDelegate()->setTitle(title);
void setTitle(base::lambda<TextWithEntities()> titleFactory) {
getDelegate()->setTitle(std::move(titleFactory));
}
void setAdditionalTitle(const QString &additional) {
getDelegate()->setAdditionalTitle(additional);
void setAdditionalTitle(base::lambda<QString()> additional) {
getDelegate()->setAdditionalTitle(std::move(additional));
}
void clearButtons() {
getDelegate()->clearButtons();
}
QPointer<Ui::RoundButton> addButton(const QString &text, base::lambda<void()> clickCallback);
QPointer<Ui::RoundButton> addLeftButton(const QString &text, base::lambda<void()> clickCallback);
QPointer<Ui::RoundButton> addButton(const QString &text, base::lambda<void()> clickCallback, const style::RoundButton &st) {
return getDelegate()->addButton(text, std::move(clickCallback), st);
QPointer<Ui::RoundButton> addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback);
QPointer<Ui::RoundButton> addLeftButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback);
QPointer<Ui::RoundButton> addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) {
return getDelegate()->addButton(std::move(textFactory), std::move(clickCallback), st);
}
void updateButtonsGeometry() {
getDelegate()->updateButtonsPositions();
@@ -108,8 +111,9 @@ public:
void setDelegate(BoxContentDelegate *newDelegate) {
_delegate = newDelegate;
_preparing = true;
prepare();
setInnerFocus();
finishPrepare();
}
public slots:
@@ -174,6 +178,8 @@ private slots:
void onDraggingScrollTimer();
private:
void finishPrepare();
void finishScrollCreate();
void setInner(object_ptr<TWidget> inner);
void setInner(object_ptr<TWidget> inner, const style::ScrollArea &st);
void updateScrollAreaGeometry();
@@ -187,6 +193,7 @@ private:
}
BoxContentDelegate *_delegate = nullptr;
bool _preparing = false;
bool _noContentMargin = false;
int _innerTopSkip = 0;
object_ptr<Ui::ScrollArea> _scroll = { nullptr };
@@ -208,13 +215,12 @@ public:
void parentResized() override;
void setLayerType(bool layerType) override;
void setTitle(const QString &title) override;
void setTitle(const TextWithEntities &title) override;
void setAdditionalTitle(const QString &additional) override;
void setTitle(base::lambda<TextWithEntities()> titleFactory) override;
void setAdditionalTitle(base::lambda<QString()> additionalFactory) override;
void clearButtons() override;
QPointer<Ui::RoundButton> addButton(const QString &text, base::lambda<void()> clickCallback, const style::RoundButton &st) override;
QPointer<Ui::RoundButton> addLeftButton(const QString &text, base::lambda<void()> clickCallback, const style::RoundButton &st) override;
QPointer<Ui::RoundButton> addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) override;
QPointer<Ui::RoundButton> addLeftButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) override;
void updateButtonsPositions() override;
void setDimensions(int newWidth, int maxHeight) override;
@@ -248,6 +254,9 @@ protected:
private:
void paintAdditionalTitle(Painter &p);
void updateTitlePosition();
void refreshTitle();
void refreshAdditionalTitle();
void refreshLang();
bool hasTitle() const;
int titleHeight() const;
@@ -266,7 +275,9 @@ private:
object_ptr<BoxContent> _content;
object_ptr<Ui::FlatLabel> _title = { nullptr };
base::lambda<TextWithEntities()> _titleFactory;
QString _additionalTitle;
base::lambda<QString()> _additionalTitleFactory;
int _titleLeft = 0;
int _titleTop = 0;
bool _layerType = false;

View File

@@ -22,8 +22,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_boxes.h"
#include "styles/style_dialogs.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "messenger.h"
#include "mtproto/sender.h"
#include "boxes/contacts_box.h"
#include "boxes/confirm_box.h"
#include "boxes/photo_crop_box.h"
@@ -40,10 +41,60 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "observer_peer.h"
#include "auth_session.h"
namespace {
constexpr auto kMaxGroupChannelTitle = 255;
constexpr auto kMaxChannelDescription = 255;
constexpr auto kMaxBioLength = 70;
style::InputField CreateBioFieldStyle() {
auto result = st::newGroupDescription;
result.textMargins.setRight(st::boxTextFont->spacew + st::boxTextFont->width(QString::number(kMaxBioLength)));
return result;
}
} // namespace
class RevokePublicLinkBox::Inner : public TWidget, private MTP::Sender {
public:
Inner(QWidget *parent, base::lambda<void()> revokeCallback);
protected:
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private:
struct ChatRow {
ChatRow(gsl::not_null<PeerData*> peer) : peer(peer) {
}
gsl::not_null<PeerData*> peer;
Text name, status;
};
void paintChat(Painter &p, const ChatRow &row, bool selected) const;
void updateSelected();
PeerData *_selected = nullptr;
PeerData *_pressed = nullptr;
std::vector<ChatRow> _rows;
int _rowsTop = 0;
int _rowHeight = 0;
int _revokeWidth = 0;
base::lambda<void()> _revokeCallback;
mtpRequestId _revokeRequestId = 0;
QPointer<ConfirmBox> _weakRevokeConfirmBox;
};
AddContactBox::AddContactBox(QWidget*, QString fname, QString lname, QString phone)
: _first(this, st::defaultInputField, lang(lng_signup_firstname), fname)
, _last(this, st::defaultInputField, lang(lng_signup_lastname), lname)
, _phone(this, st::defaultInputField, lang(lng_contact_phone), phone)
: _first(this, st::defaultInputField, langFactory(lng_signup_firstname), fname)
, _last(this, st::defaultInputField, langFactory(lng_signup_lastname), lname)
, _phone(this, st::defaultInputField, langFactory(lng_contact_phone), phone)
, _invertOrder(langFirstNameGoesSecond()) {
if (!phone.isEmpty()) {
_phone->setDisabled(true);
@@ -52,9 +103,9 @@ AddContactBox::AddContactBox(QWidget*, QString fname, QString lname, QString pho
AddContactBox::AddContactBox(QWidget*, UserData *user)
: _user(user)
, _first(this, st::defaultInputField, lang(lng_signup_firstname), user->firstName)
, _last(this, st::defaultInputField, lang(lng_signup_lastname), user->lastName)
, _phone(this, st::defaultInputField, lang(lng_contact_phone), user->phone())
, _first(this, st::defaultInputField, langFactory(lng_signup_firstname), user->firstName)
, _last(this, st::defaultInputField, langFactory(lng_signup_lastname), user->lastName)
, _phone(this, st::defaultInputField, langFactory(lng_contact_phone), user->phone())
, _invertOrder(langFirstNameGoesSecond()) {
_phone->setDisabled(true);
}
@@ -64,10 +115,10 @@ void AddContactBox::prepare() {
setTabOrder(_last, _first);
}
if (_user) {
setTitle(lang(lng_edit_contact_title));
setTitle(langFactory(lng_edit_contact_title));
} else {
bool readyToAdd = !_phone->getLastText().isEmpty() && (!_first->getLastText().isEmpty() || !_last->getLastText().isEmpty());
setTitle(lang(readyToAdd ? lng_confirm_contact_data : lng_enter_contact_data));
auto readyToAdd = !_phone->getLastText().isEmpty() && (!_first->getLastText().isEmpty() || !_last->getLastText().isEmpty());
setTitle(langFactory(readyToAdd ? lng_confirm_contact_data : lng_enter_contact_data));
}
updateButtons();
@@ -136,9 +187,9 @@ void AddContactBox::onSubmit() {
void AddContactBox::onSave() {
if (_addRequest) return;
QString firstName = prepareText(_first->getLastText());
QString lastName = prepareText(_last->getLastText());
QString phone = _phone->getLastText().trimmed();
auto firstName = TextUtilities::PrepareForSending(_first->getLastText());
auto lastName = TextUtilities::PrepareForSending(_last->getLastText());
auto phone = _phone->getLastText().trimmed();
if (firstName.isEmpty() && lastName.isEmpty()) {
if (_invertOrder) {
_last->setFocus();
@@ -236,10 +287,10 @@ void AddContactBox::onRetry() {
void AddContactBox::updateButtons() {
clearButtons();
if (_retrying) {
addButton(lang(lng_try_other_contact), [this] { onRetry(); });
addButton(langFactory(lng_try_other_contact), [this] { onRetry(); });
} else {
addButton(lang(_user ? lng_settings_save : lng_add_contact), [this] { onSave(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(_user ? lng_settings_save : lng_add_contact), [this] { onSave(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
}
}
@@ -247,18 +298,18 @@ GroupInfoBox::GroupInfoBox(QWidget*, CreatingGroupType creating, bool fromTypeCh
: _creating(creating)
, _fromTypeChoose(fromTypeChoose)
, _photo(this, st::newGroupPhotoSize, st::newGroupPhotoIconPosition)
, _title(this, st::defaultInputField, lang(_creating == CreatingGroupChannel ? lng_dlg_new_channel_name : lng_dlg_new_group_name)) {
, _title(this, st::defaultInputField, langFactory(_creating == CreatingGroupChannel ? lng_dlg_new_channel_name : lng_dlg_new_group_name)) {
}
void GroupInfoBox::prepare() {
setMouseTracking(true);
_title->setMaxLength(MaxGroupChannelTitle);
_title->setMaxLength(kMaxGroupChannelTitle);
if (_creating == CreatingGroupChannel) {
_description.create(this, st::newGroupDescription, lang(lng_create_group_description));
_description.create(this, st::newGroupDescription, langFactory(lng_create_group_description));
_description->show();
_description->setMaxLength(MaxChannelDescription);
_description->setMaxLength(kMaxChannelDescription);
connect(_description, SIGNAL(resized()), this, SLOT(onDescriptionResized()));
connect(_description, SIGNAL(submitted(bool)), this, SLOT(onNext()));
@@ -267,8 +318,8 @@ void GroupInfoBox::prepare() {
connect(_title, SIGNAL(submitted(bool)), this, SLOT(onNameSubmit()));
addButton(lang(_creating == CreatingGroupChannel ? lng_create_group_create : lng_create_group_next), [this] { onNext(); });
addButton(lang(_fromTypeChoose ? lng_create_group_back : lng_cancel), [this] { closeBox(); });
addButton(langFactory(_creating == CreatingGroupChannel ? lng_create_group_create : lng_create_group_next), [this] { onNext(); });
addButton(langFactory(_fromTypeChoose ? lng_create_group_back : lng_cancel), [this] { closeBox(); });
setupPhotoButton();
@@ -331,8 +382,8 @@ void GroupInfoBox::onNameSubmit() {
void GroupInfoBox::onNext() {
if (_creationRequestId) return;
auto title = prepareText(_title->getLastText());
auto description = _description ? prepareText(_description->getLastText(), true) : QString();
auto title = TextUtilities::PrepareForSending(_title->getLastText());
auto description = _description ? TextUtilities::PrepareForSending(_description->getLastText(), TextUtilities::PrepareTextOption::CheckLinks) : QString();
if (title.isEmpty()) {
_title->setFocus();
_title->showError();
@@ -422,10 +473,10 @@ SetupChannelBox::SetupChannelBox(QWidget*, ChannelData *channel, bool existing)
, _privacyGroup(std::make_shared<Ui::RadioenumGroup<Privacy>>(Privacy::Public))
, _public(this, _privacyGroup, Privacy::Public, lang(channel->isMegagroup() ? lng_create_public_group_title : lng_create_public_channel_title), st::defaultBoxCheckbox)
, _private(this, _privacyGroup, Privacy::Private, lang(channel->isMegagroup() ? lng_create_private_group_title : lng_create_private_channel_title), st::defaultBoxCheckbox)
, _aboutPublicWidth(st::boxWideWidth - st::boxPadding.left() - st::boxButtonPadding.right() - st::newGroupPadding.left() - st::defaultBoxCheckbox.textPosition.x())
, _aboutPublicWidth(st::boxWideWidth - st::boxPadding.left() - st::boxButtonPadding.right() - st::newGroupPadding.left() - st::defaultRadio.diameter - st::defaultBoxCheckbox.textPosition.x())
, _aboutPublic(st::defaultTextStyle, lang(channel->isMegagroup() ? lng_create_public_group_about : lng_create_public_channel_about), _defaultOptions, _aboutPublicWidth)
, _aboutPrivate(st::defaultTextStyle, lang(channel->isMegagroup() ? lng_create_private_group_about : lng_create_private_channel_about), _defaultOptions, _aboutPublicWidth)
, _link(this, st::setupChannelLink, QString(), channel->username, true) {
, _link(this, st::setupChannelLink, base::lambda<QString()>(), channel->username, true) {
}
void SetupChannelBox::prepare() {
@@ -435,8 +486,8 @@ void SetupChannelBox::prepare() {
_checkRequestId = MTP::send(MTPchannels_CheckUsername(_channel->inputChannel, MTP_string("preston")), RPCDoneHandlerPtr(), rpcFail(&SetupChannelBox::onFirstCheckFail));
addButton(lang(lng_settings_save), [this] { onSave(); });
addButton(lang(_existing ? lng_cancel : lng_create_group_skip), [this] { closeBox(); });
addButton(langFactory(lng_settings_save), [this] { onSave(); });
addButton(langFactory(_existing ? lng_cancel : lng_create_group_skip), [this] { closeBox(); });
connect(_link, SIGNAL(changed()), this, SLOT(onChange()));
_link->setVisible(_privacyGroup->value() == Privacy::Public);
@@ -445,6 +496,11 @@ void SetupChannelBox::prepare() {
connect(&_checkTimer, SIGNAL(timeout()), this, SLOT(onCheck()));
_privacyGroup->setChangedCallback([this](Privacy value) { privacyChanged(value); });
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::InviteLinkChanged, [this](const Notify::PeerUpdate &update) {
if (update.peer == _channel) {
rtlupdate(_invitationLink);
}
}));
updateMaxHeight();
}
@@ -486,10 +542,10 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) {
p.fillRect(e->rect(), st::boxBg);
p.setPen(st::newGroupAboutFg);
QRect aboutPublic(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultBoxCheckbox.textPosition.x(), _public->bottomNoMargins(), _aboutPublicWidth, _aboutPublicHeight);
QRect aboutPublic(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultRadio.diameter + st::defaultBoxCheckbox.textPosition.x(), _public->bottomNoMargins(), _aboutPublicWidth, _aboutPublicHeight);
_aboutPublic.drawLeft(p, aboutPublic.x(), aboutPublic.y(), aboutPublic.width(), width());
QRect aboutPrivate(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultBoxCheckbox.textPosition.x(), _private->bottomNoMargins(), _aboutPublicWidth, _aboutPublicHeight);
QRect aboutPrivate(st::boxPadding.left() + st::newGroupPadding.left() + st::defaultRadio.diameter + st::defaultBoxCheckbox.textPosition.x(), _private->bottomNoMargins(), _aboutPublicWidth, _aboutPublicHeight);
_aboutPrivate.drawLeft(p, aboutPrivate.x(), aboutPrivate.y(), aboutPrivate.width(), width());
if (!_channel->isMegagroup() || !_link->isHidden()) {
@@ -504,7 +560,8 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) {
option.setWrapMode(QTextOption::WrapAnywhere);
p.setFont(_linkOver ? st::boxTextFont->underline() : st::boxTextFont);
p.setPen(st::defaultLinkButton.color);
p.drawText(_invitationLink, _channel->inviteLink(), option);
auto inviteLinkText = _channel->inviteLink().isEmpty() ? lang(lng_group_invite_create) : _channel->inviteLink();
p.drawText(_invitationLink, inviteLinkText, option);
}
} else {
if (!_errorText.isEmpty()) {
@@ -536,8 +593,12 @@ void SetupChannelBox::mouseMoveEvent(QMouseEvent *e) {
void SetupChannelBox::mousePressEvent(QMouseEvent *e) {
if (_linkOver) {
QGuiApplication::clipboard()->setText(_channel->inviteLink());
Ui::Toast::Show(lang(lng_create_channel_link_copied));
if (_channel->inviteLink().isEmpty()) {
App::api()->exportInviteLink(_channel);
} else {
QGuiApplication::clipboard()->setText(_channel->inviteLink());
Ui::Toast::Show(lang(lng_create_channel_link_copied));
}
}
}
@@ -658,7 +719,7 @@ void SetupChannelBox::privacyChanged(Privacy value) {
}
void SetupChannelBox::onUpdateDone(const MTPBool &result) {
_channel->setName(textOneLine(_channel->name), _sentUsername);
_channel->setName(TextUtilities::SingleLine(_channel->name), _sentUsername);
closeBox();
}
@@ -668,7 +729,7 @@ bool SetupChannelBox::onUpdateFail(const RPCError &error) {
_saveRequestId = 0;
QString err(error.type());
if (err == "USERNAME_NOT_MODIFIED" || _sentUsername == _channel->username) {
_channel->setName(textOneLine(_channel->name), textOneLine(_sentUsername));
_channel->setName(TextUtilities::SingleLine(_channel->name), TextUtilities::SingleLine(_sentUsername));
closeBox();
return true;
} else if (err == "USERNAME_INVALID") {
@@ -758,31 +819,31 @@ bool SetupChannelBox::onFirstCheckFail(const RPCError &error) {
return true;
}
EditNameTitleBox::EditNameTitleBox(QWidget*, PeerData *peer)
EditNameTitleBox::EditNameTitleBox(QWidget*, gsl::not_null<PeerData*> peer)
: _peer(peer)
, _first(this, st::defaultInputField, lang(peer->isUser() ? lng_signup_firstname : lng_dlg_new_group_name), peer->isUser() ? peer->asUser()->firstName : peer->name)
, _last(this, st::defaultInputField, lang(lng_signup_lastname), peer->isUser() ? peer->asUser()->lastName : QString())
, _first(this, st::defaultInputField, langFactory(_peer->isUser() ? lng_signup_firstname : lng_dlg_new_group_name), _peer->isUser() ? _peer->asUser()->firstName : _peer->name)
, _last(this, st::defaultInputField, langFactory(lng_signup_lastname), peer->isUser() ? peer->asUser()->lastName : QString())
, _invertOrder(!peer->isChat() && langFirstNameGoesSecond()) {
}
void EditNameTitleBox::prepare() {
auto newHeight = st::contactPadding.top() + _first->height();
if (_peer->isUser()) {
setTitle(lang(_peer == App::self() ? lng_edit_self_title : lng_edit_contact_title));
setTitle(langFactory(_peer->isSelf() ? lng_edit_self_title : lng_edit_contact_title));
newHeight += st::contactSkip + _last->height();
} else if (_peer->isChat()) {
setTitle(lang(lng_edit_group_title));
setTitle(langFactory(lng_edit_group_title));
}
newHeight += st::boxPadding.bottom() + st::contactPadding.bottom();
setDimensions(st::boxWideWidth, newHeight);
addButton(lang(lng_settings_save), [this] { onSave(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_settings_save), [this] { onSave(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
if (_invertOrder) {
setTabOrder(_last, _first);
}
_first->setMaxLength(MaxGroupChannelTitle);
_last->setMaxLength(MaxGroupChannelTitle);
_first->setMaxLength(kMaxGroupChannelTitle);
_last->setMaxLength(kMaxGroupChannelTitle);
connect(_first, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
connect(_last, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
@@ -835,7 +896,8 @@ void EditNameTitleBox::resizeEvent(QResizeEvent *e) {
void EditNameTitleBox::onSave() {
if (_requestId) return;
QString first = prepareText(_first->getLastText()), last = prepareText(_last->getLastText());
auto first = TextUtilities::PrepareForSending(_first->getLastText());
auto last = TextUtilities::PrepareForSending(_last->getLastText());
if (first.isEmpty() && last.isEmpty()) {
if (_invertOrder) {
_last->setFocus();
@@ -867,10 +929,11 @@ void EditNameTitleBox::onSaveSelfDone(const MTPUser &user) {
bool EditNameTitleBox::onSaveSelfFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
QString err(error.type());
QString first = textOneLine(_first->getLastText().trimmed()), last = textOneLine(_last->getLastText().trimmed());
auto err = error.type();
auto first = TextUtilities::SingleLine(_first->getLastText().trimmed());
auto last = TextUtilities::SingleLine(_last->getLastText().trimmed());
if (err == "NAME_NOT_MODIFIED") {
App::self()->setName(first, last, QString(), textOneLine(App::self()->username));
App::self()->setName(first, last, QString(), TextUtilities::SingleLine(App::self()->username));
closeBox();
return true;
} else if (err == "FIRSTNAME_INVALID") {
@@ -911,26 +974,99 @@ void EditNameTitleBox::onSaveChatDone(const MTPUpdates &updates) {
closeBox();
}
EditChannelBox::EditChannelBox(QWidget*, ChannelData *channel)
EditBioBox::EditBioBox(QWidget*, gsl::not_null<UserData*> self) : BoxContent()
, _dynamicFieldStyle(CreateBioFieldStyle())
, _self(self)
, _bio(this, _dynamicFieldStyle, langFactory(lng_bio_placeholder), _self->about())
, _countdown(this, QString(), Ui::FlatLabel::InitType::Simple, st::editBioCountdownLabel)
, _about(this, lang(lng_bio_about), Ui::FlatLabel::InitType::Simple, st::aboutRevokePublicLabel) {
}
void EditBioBox::prepare() {
setTitle(langFactory(lng_bio_title));
addButton(langFactory(lng_settings_save), [this] { save(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
_bio->setMaxLength(kMaxBioLength);
_bio->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
auto cursor = _bio->textCursor();
cursor.setPosition(_bio->getLastText().size());
_bio->setTextCursor(cursor);
connect(_bio, &Ui::InputArea::submitted, this, [this](bool ctrlShiftEnter) { save(); });
connect(_bio, &Ui::InputArea::resized, this, [this] { updateMaxHeight(); });
connect(_bio, &Ui::InputArea::changed, this, [this] { handleBioUpdated(); });
handleBioUpdated();
updateMaxHeight();
}
void EditBioBox::updateMaxHeight() {
auto newHeight = st::contactPadding.top() + _bio->height() + st::boxLittleSkip + _about->height() + st::boxPadding.bottom() + st::contactPadding.bottom();
setDimensions(st::boxWideWidth, newHeight);
}
void EditBioBox::handleBioUpdated() {
auto text = _bio->getLastText();
if (text.indexOf('\n') >= 0) {
auto position = _bio->textCursor().position();
_bio->setText(text.replace('\n', ' '));
auto cursor = _bio->textCursor();
cursor.setPosition(position);
_bio->setTextCursor(cursor);
}
auto countLeft = qMax(kMaxBioLength - text.size(), 0);
_countdown->setText(QString::number(countLeft));
}
void EditBioBox::setInnerFocus() {
_bio->setFocusFast();
}
void EditBioBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_bio->resize(width() - st::boxPadding.left() - st::newGroupInfoPadding.left() - st::boxPadding.right(), _bio->height());
_bio->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), st::contactPadding.top());
_countdown->moveToRight(st::boxPadding.right(), _bio->y() + _dynamicFieldStyle.textMargins.top());
_about->moveToLeft(st::boxPadding.left(), _bio->y() + _bio->height() + st::boxLittleSkip);
}
void EditBioBox::save() {
if (_requestId) return;
auto text = TextUtilities::PrepareForSending(_bio->getLastText());
_sentBio = text;
auto flags = MTPaccount_UpdateProfile::Flag::f_about;
_requestId = request(MTPaccount_UpdateProfile(MTP_flags(flags), MTPstring(), MTPstring(), MTP_string(text))).done([this](const MTPUser &result) {
App::feedUsers(MTP_vector<MTPUser>(1, result));
_self->setAbout(_sentBio);
closeBox();
}).send();
}
EditChannelBox::EditChannelBox(QWidget*, gsl::not_null<ChannelData*> channel)
: _channel(channel)
, _title(this, st::defaultInputField, lang(channel->isMegagroup() ? lng_dlg_new_group_name : lng_dlg_new_channel_name), _channel->name)
, _description(this, st::newGroupDescription, lang(lng_create_group_description), _channel->about())
, _title(this, st::defaultInputField, langFactory(_channel->isMegagroup() ? lng_dlg_new_group_name : lng_dlg_new_channel_name), _channel->name)
, _description(this, st::newGroupDescription, langFactory(lng_create_group_description), _channel->about())
, _sign(this, lang(lng_edit_sign_messages), channel->addsSignature(), st::defaultBoxCheckbox)
, _inviteGroup(std::make_shared<Ui::RadioenumGroup<Invites>>(channel->anyoneCanAddMembers() ? Invites::Everybody : Invites::OnlyAdmins))
, _inviteEverybody(this, _inviteGroup, Invites::Everybody, lang(lng_edit_group_invites_everybody))
, _inviteOnlyAdmins(this, _inviteGroup, Invites::OnlyAdmins, lang(lng_edit_group_invites_only_admins))
, _publicLink(this, lang(channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link), st::boxLinkButton) {
}
void EditChannelBox::prepare() {
setTitle(lang(_channel->isMegagroup() ? lng_edit_group : lng_edit_channel_title));
setTitle(langFactory(_channel->isMegagroup() ? lng_edit_group : lng_edit_channel_title));
addButton(lang(lng_settings_save), [this] { onSave(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_settings_save), [this] { onSave(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
connect(App::main(), SIGNAL(peerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)), this, SLOT(peerUpdated(PeerData*)));
setMouseTracking(true);
_title->setMaxLength(MaxGroupChannelTitle);
_description->setMaxLength(MaxChannelDescription);
_title->setMaxLength(kMaxGroupChannelTitle);
_description->setMaxLength(kMaxChannelDescription);
connect(_description, SIGNAL(resized()), this, SLOT(onDescriptionResized()));
connect(_description, SIGNAL(submitted(bool)), this, SLOT(onSave()));
@@ -938,7 +1074,9 @@ void EditChannelBox::prepare() {
connect(_publicLink, SIGNAL(clicked()), this, SLOT(onPublicLink()));
_publicLink->setVisible(_channel->canEditUsername());
_sign->setVisible(!_channel->isMegagroup());
_sign->setVisible(canEditSignatures());
_inviteEverybody->setVisible(canEditInvites());
_inviteOnlyAdmins->setVisible(canEditInvites());
updateMaxHeight();
}
@@ -969,12 +1107,24 @@ void EditChannelBox::onDescriptionResized() {
update();
}
bool EditChannelBox::canEditSignatures() const {
return _channel->canEditInformation() && !_channel->isMegagroup();
}
bool EditChannelBox::canEditInvites() const {
return _channel->canEditInformation() && _channel->isMegagroup();
}
void EditChannelBox::updateMaxHeight() {
auto newHeight = st::newGroupInfoPadding.top() + _title->height();
newHeight += st::newGroupDescriptionPadding.top() + _description->height() + st::newGroupDescriptionPadding.bottom();
if (!_channel->isMegagroup()) {
if (canEditSignatures()) {
newHeight += st::newGroupPublicLinkPadding.top() + _sign->heightNoMargins() + st::newGroupPublicLinkPadding.bottom();
}
if (canEditInvites()) {
newHeight += st::boxTitleHeight + _inviteEverybody->heightNoMargins();
newHeight += st::boxLittleSkip + _inviteOnlyAdmins->heightNoMargins();
}
if (_channel->canEditUsername()) {
newHeight += st::newGroupPublicLinkPadding.top() + _publicLink->height() + st::newGroupPublicLinkPadding.bottom();
}
@@ -993,17 +1143,34 @@ void EditChannelBox::resizeEvent(QResizeEvent *e) {
_sign->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description->y() + _description->height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top());
if (_channel->isMegagroup()) {
_publicLink->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description->y() + _description->height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top());
} else {
_inviteEverybody->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description->y() + _description->height() + st::boxTitleHeight);
_inviteOnlyAdmins->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _inviteEverybody->bottomNoMargins() + st::boxLittleSkip);
if (canEditSignatures()) {
_publicLink->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _sign->bottomNoMargins() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top());
} else if (canEditInvites()) {
_publicLink->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _inviteOnlyAdmins->bottomNoMargins() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top());
} else {
_publicLink->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description->y() + _description->height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top());
}
}
void EditChannelBox::paintEvent(QPaintEvent *e) {
BoxContent::paintEvent(e);
if (canEditInvites()) {
Painter p(this);
p.setPen(st::boxTitleFg);
p.setFont(st::autoDownloadTitleFont);
p.drawTextLeft(st::boxTitlePosition.x(), _description->y() + _description->height() + st::boxTitlePosition.y(), width(), lang(lng_edit_group_who_invites));
}
}
void EditChannelBox::onSave() {
if (_saveTitleRequestId || _saveDescriptionRequestId || _saveSignRequestId) return;
if (_saveTitleRequestId || _saveDescriptionRequestId || _saveSignRequestId || _saveInvitesRequestId) return;
QString title = prepareText(_title->getLastText()), description = prepareText(_description->getLastText(), true);
auto title = TextUtilities::PrepareForSending(_title->getLastText());
auto description = TextUtilities::PrepareForSending(_description->getLastText(), TextUtilities::PrepareTextOption::CheckLinks);
if (title.isEmpty()) {
_title->setFocus();
_title->showError();
@@ -1031,13 +1198,21 @@ void EditChannelBox::saveDescription() {
}
void EditChannelBox::saveSign() {
if (_channel->isMegagroup() || _channel->addsSignature() == _sign->checked()) {
closeBox();
if (!canEditSignatures() || _channel->addsSignature() == _sign->checked()) {
saveInvites();
} else {
_saveSignRequestId = MTP::send(MTPchannels_ToggleSignatures(_channel->inputChannel, MTP_bool(_sign->checked())), rpcDone(&EditChannelBox::onSaveSignDone), rpcFail(&EditChannelBox::onSaveFail));
}
}
void EditChannelBox::saveInvites() {
if (!canEditInvites() || _channel->anyoneCanAddMembers() == (_inviteGroup->value() == Invites::Everybody)) {
closeBox();
} else {
_saveInvitesRequestId = MTP::send(MTPchannels_ToggleInvites(_channel->inputChannel, MTP_bool(_inviteGroup->value() == Invites::Everybody)), rpcDone(&EditChannelBox::onSaveInvitesDone), rpcFail(&EditChannelBox::onSaveFail));
}
}
bool EditChannelBox::onSaveFail(const RPCError &error, mtpRequestId req) {
if (MTP::isDefaultHandledError(error)) return false;
@@ -1070,6 +1245,12 @@ bool EditChannelBox::onSaveFail(const RPCError &error, mtpRequestId req) {
}
} else if (req == _saveSignRequestId) {
_saveSignRequestId = 0;
if (err == qstr("CHAT_NOT_MODIFIED")) {
saveInvites();
return true;
}
} else if (req == _saveInvitesRequestId) {
_saveInvitesRequestId = 0;
if (err == qstr("CHAT_NOT_MODIFIED")) {
closeBox();
return true;
@@ -1078,11 +1259,9 @@ bool EditChannelBox::onSaveFail(const RPCError &error, mtpRequestId req) {
return true;
}
void EditChannelBox::onSaveTitleDone(const MTPUpdates &updates) {
void EditChannelBox::onSaveTitleDone(const MTPUpdates &result) {
_saveTitleRequestId = 0;
if (App::main()) {
App::main()->sentUpdatesReceived(updates);
}
AuthSession::Current().api().applyUpdates(result);
saveDescription();
}
@@ -1096,43 +1275,74 @@ void EditChannelBox::onSaveDescriptionDone(const MTPBool &result) {
saveSign();
}
void EditChannelBox::onSaveSignDone(const MTPUpdates &updates) {
void EditChannelBox::onSaveSignDone(const MTPUpdates &result) {
_saveSignRequestId = 0;
if (App::main()) {
App::main()->sentUpdatesReceived(updates);
}
AuthSession::Current().api().applyUpdates(result);
saveInvites();
}
void EditChannelBox::onSaveInvitesDone(const MTPUpdates &result) {
_saveSignRequestId = 0;
AuthSession::Current().api().applyUpdates(result);
closeBox();
}
RevokePublicLinkBox::RevokePublicLinkBox(QWidget*, base::lambda<void()> revokeCallback)
: _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
RevokePublicLinkBox::Inner::Inner(QWidget *parent, base::lambda<void()> revokeCallback) : TWidget(parent)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _revokeWidth(st::normalFont->width(lang(lng_channels_too_much_public_revoke)))
, _aboutRevoke(this, lang(lng_channels_too_much_public_about), Ui::FlatLabel::InitType::Simple, st::aboutRevokePublicLabel)
, _revokeCallback(std::move(revokeCallback)) {
setMouseTracking(true);
resize(width(), 5 * _rowHeight);
request(MTPchannels_GetAdminedPublicChannels()).done([this](const MTPmessages_Chats &result) {
if (auto chats = Api::getChatsFromMessagesChats(result)) {
for_const (auto &chat, chats->v) {
if (auto peer = App::feedChat(chat)) {
if (!peer->isChannel() || peer->userName().isEmpty()) {
continue;
}
auto row = ChatRow(peer);
row.peer = peer;
row.name.setText(st::contactsNameStyle, peer->name, _textNameOptions);
row.status.setText(st::defaultTextStyle, Messenger::Instance().createInternalLink(textcmdLink(1, peer->userName())), _textDlgOptions);
_rows.push_back(std::move(row));
}
}
}
resize(width(), _rows.size() * _rowHeight);
update();
}).send();
}
RevokePublicLinkBox::RevokePublicLinkBox(QWidget*, base::lambda<void()> revokeCallback)
: _aboutRevoke(this, lang(lng_channels_too_much_public_about), Ui::FlatLabel::InitType::Simple, st::aboutRevokePublicLabel)
, _revokeCallback(std::move(revokeCallback)) {
}
void RevokePublicLinkBox::prepare() {
setMouseTracking(true);
_innerTop = st::boxPadding.top() + _aboutRevoke->height() + st::boxPadding.top();
_inner = setInnerWidget(object_ptr<Inner>(this, [this] {
closeBox();
if (_revokeCallback) {
_revokeCallback();
}
}), st::boxLayerScroll, _innerTop);
MTP::send(MTPchannels_GetAdminedPublicChannels(), rpcDone(&RevokePublicLinkBox::getPublicDone), rpcFail(&RevokePublicLinkBox::getPublicFail));
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); });
updateMaxHeight();
_inner->resizeToWidth(st::boxWideWidth);
setDimensions(st::boxWideWidth, _innerTop + _inner->height());
}
void RevokePublicLinkBox::updateMaxHeight() {
_rowsTop = st::boxPadding.top() + _aboutRevoke->height() + st::boxPadding.top();
setDimensions(st::boxWideWidth, _rowsTop + (5 * _rowHeight));
}
void RevokePublicLinkBox::mouseMoveEvent(QMouseEvent *e) {
void RevokePublicLinkBox::Inner::mouseMoveEvent(QMouseEvent *e) {
updateSelected();
}
void RevokePublicLinkBox::updateSelected() {
void RevokePublicLinkBox::Inner::updateSelected() {
auto point = mapFromGlobal(QCursor::pos());
PeerData *selected = nullptr;
auto top = _rowsTop;
@@ -1151,14 +1361,14 @@ void RevokePublicLinkBox::updateSelected() {
}
}
void RevokePublicLinkBox::mousePressEvent(QMouseEvent *e) {
void RevokePublicLinkBox::Inner::mousePressEvent(QMouseEvent *e) {
if (_pressed != _selected) {
_pressed = _selected;
update();
}
}
void RevokePublicLinkBox::mouseReleaseEvent(QMouseEvent *e) {
void RevokePublicLinkBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
auto pressed = base::take(_pressed);
setCursor((_selected || _pressed) ? style::cur_pointer : style::cur_default);
if (pressed && pressed == _selected) {
@@ -1167,14 +1377,19 @@ void RevokePublicLinkBox::mouseReleaseEvent(QMouseEvent *e) {
auto confirmText = lang(lng_channels_too_much_public_revoke);
_weakRevokeConfirmBox = Ui::show(Box<ConfirmBox>(text, confirmText, base::lambda_guarded(this, [this, pressed]() {
if (_revokeRequestId) return;
_revokeRequestId = MTP::send(MTPchannels_UpdateUsername(pressed->asChannel()->inputChannel, MTP_string("")), rpcDone(&RevokePublicLinkBox::revokeLinkDone), rpcFail(&RevokePublicLinkBox::revokeLinkFail));
_revokeRequestId = request(MTPchannels_UpdateUsername(pressed->asChannel()->inputChannel, MTP_string(""))).done([this](const MTPBool &result) {
if (_weakRevokeConfirmBox) {
_weakRevokeConfirmBox->closeBox();
}
if (_revokeCallback) {
_revokeCallback();
}
}).send();
})), KeepOtherLayers);
}
}
void RevokePublicLinkBox::paintEvent(QPaintEvent *e) {
BoxContent::paintEvent(e);
void RevokePublicLinkBox::Inner::paintEvent(QPaintEvent *e) {
Painter p(this);
p.translate(0, _rowsTop);
for_const (auto &row, _rows) {
@@ -1189,7 +1404,7 @@ void RevokePublicLinkBox::resizeEvent(QResizeEvent *e) {
_aboutRevoke->moveToLeft(st::boxPadding.left(), st::boxPadding.top());
}
void RevokePublicLinkBox::paintChat(Painter &p, const ChatRow &row, bool selected) const {
void RevokePublicLinkBox::Inner::paintChat(Painter &p, const ChatRow &row, bool selected) const {
auto peer = row.peer;
peer->paintUserpicLeft(p, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize);
@@ -1213,46 +1428,3 @@ void RevokePublicLinkBox::paintChat(Painter &p, const ChatRow &row, bool selecte
row.status.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsStatusTop, namew, width());
p.restoreTextPalette();
}
void RevokePublicLinkBox::getPublicDone(const MTPmessages_Chats &result) {
if (auto chats = Api::getChatsFromMessagesChats(result)) {
for_const (auto &chat, chats->v) {
if (auto peer = App::feedChat(chat)) {
if (!peer->isChannel() || peer->userName().isEmpty()) continue;
ChatRow row;
row.peer = peer;
row.name.setText(st::contactsNameStyle, peer->name, _textNameOptions);
row.status.setText(st::defaultTextStyle, Messenger::Instance().createInternalLink(textcmdLink(1, peer->userName())), _textDlgOptions);
_rows.push_back(std::move(row));
}
}
}
update();
}
bool RevokePublicLinkBox::getPublicFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) {
return false;
}
return true;
}
void RevokePublicLinkBox::revokeLinkDone(const MTPBool &result) {
if (_weakRevokeConfirmBox) {
_weakRevokeConfirmBox->closeBox();
}
closeBox();
if (_revokeCallback) {
_revokeCallback();
}
}
bool RevokePublicLinkBox::revokeLinkFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) {
return false;
}
return true;
}

View File

@@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once
#include "boxes/abstract_box.h"
#include "mtproto/sender.h"
class ConfirmBox;
@@ -199,7 +200,7 @@ class EditNameTitleBox : public BoxContent, public RPCSender {
Q_OBJECT
public:
EditNameTitleBox(QWidget*, PeerData *peer);
EditNameTitleBox(QWidget*, gsl::not_null<PeerData*> peer);
protected:
void setInnerFocus() override;
@@ -218,7 +219,7 @@ private:
void onSaveChatDone(const MTPUpdates &updates);
bool onSaveChatFail(const RPCError &e);
PeerData *_peer;
gsl::not_null<PeerData*> _peer;
object_ptr<Ui::InputField> _first;
object_ptr<Ui::InputField> _last;
@@ -230,11 +231,37 @@ private:
};
class EditBioBox : public BoxContent, private MTP::Sender {
public:
EditBioBox(QWidget*, gsl::not_null<UserData*> self);
protected:
void setInnerFocus() override;
void prepare() override;
void resizeEvent(QResizeEvent *e) override;
private:
void updateMaxHeight();
void handleBioUpdated();
void save();
style::InputField _dynamicFieldStyle;
gsl::not_null<UserData*> _self;
object_ptr<Ui::InputArea> _bio;
object_ptr<Ui::FlatLabel> _countdown;
object_ptr<Ui::FlatLabel> _about;
mtpRequestId _requestId = 0;
QString _sentBio;
};
class EditChannelBox : public BoxContent, public RPCSender {
Q_OBJECT
public:
EditChannelBox(QWidget*, ChannelData *channel);
EditChannelBox(QWidget*, gsl::not_null<ChannelData*> channel);
protected:
void prepare() override;
@@ -242,6 +269,7 @@ protected:
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private slots:
void peerUpdated(PeerData *peer);
@@ -255,26 +283,39 @@ private slots:
private:
void updateMaxHeight();
bool canEditSignatures() const;
bool canEditInvites() const;
void onSaveTitleDone(const MTPUpdates &updates);
void onSaveTitleDone(const MTPUpdates &result);
void onSaveDescriptionDone(const MTPBool &result);
void onSaveSignDone(const MTPUpdates &updates);
bool onSaveFail(const RPCError &e, mtpRequestId req);
void onSaveSignDone(const MTPUpdates &result);
void onSaveInvitesDone(const MTPUpdates &result);
bool onSaveFail(const RPCError &error, mtpRequestId req);
void saveDescription();
void saveSign();
void saveInvites();
ChannelData *_channel;
gsl::not_null<ChannelData*> _channel;
object_ptr<Ui::InputField> _title;
object_ptr<Ui::InputArea> _description;
object_ptr<Ui::Checkbox> _sign;
enum class Invites {
Everybody,
OnlyAdmins,
};
std::shared_ptr<Ui::RadioenumGroup<Invites>> _inviteGroup;
object_ptr<Ui::Radioenum<Invites>> _inviteEverybody;
object_ptr<Ui::Radioenum<Invites>> _inviteOnlyAdmins;
object_ptr<Ui::LinkButton> _publicLink;
mtpRequestId _saveTitleRequestId = 0;
mtpRequestId _saveDescriptionRequestId = 0;
mtpRequestId _saveSignRequestId = 0;
mtpRequestId _saveInvitesRequestId = 0;
QString _sentTitle, _sentDescription;
@@ -287,41 +328,15 @@ public:
protected:
void prepare() override;
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
void updateMaxHeight();
void updateSelected();
struct ChatRow {
PeerData *peer;
Text name, status;
};
void paintChat(Painter &p, const ChatRow &row, bool selected) const;
void getPublicDone(const MTPmessages_Chats &result);
bool getPublicFail(const RPCError &error);
void revokeLinkDone(const MTPBool &result);
bool revokeLinkFail(const RPCError &error);
PeerData *_selected = nullptr;
PeerData *_pressed = nullptr;
QVector<ChatRow> _rows;
int _rowsTop = 0;
int _rowHeight = 0;
int _revokeWidth = 0;
object_ptr<Ui::FlatLabel> _aboutRevoke;
class Inner;
QPointer<Inner> _inner;
int _innerTop = 0;
base::lambda<void()> _revokeCallback;
mtpRequestId _revokeRequestId = 0;
QPointer<ConfirmBox> _weakRevokeConfirmBox;
};

View File

@@ -20,31 +20,31 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/autolock_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "mainwindow.h"
#include "ui/widgets/checkbox.h"
#include "styles/style_boxes.h"
void AutoLockBox::prepare() {
setTitle(lang(lng_passcode_autolock));
setTitle(langFactory(lng_passcode_autolock));
addButton(lang(lng_box_ok), [this] { closeBox(); });
addButton(langFactory(lng_box_ok), [this] { closeBox(); });
auto options = { 60, 300, 3600, 18000 };
auto group = std::make_shared<Ui::RadiobuttonGroup>(Global::AutoLock());
auto y = st::boxOptionListPadding.top();
auto y = st::boxOptionListPadding.top() + st::langsButton.margin.top();
auto count = int(options.size());
_options.reserve(count);
for (auto seconds : options) {
_options.emplace_back(this, group, seconds, (seconds % 3600) ? lng_passcode_autolock_minutes(lt_count, seconds / 60) : lng_passcode_autolock_hours(lt_count, seconds / 3600), st::langsButton);
_options.back()->move(st::boxPadding.left() + st::boxOptionListPadding.left(), y);
_options.back()->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), y);
y += _options.back()->heightNoMargins() + st::boxOptionListSkip;
}
group->setChangedCallback([this](int value) { durationChanged(value); });
setDimensions(st::langsWidth, st::boxOptionListPadding.top() + count * st::langsButton.height + (count - 1) * st::boxOptionListSkip + st::boxOptionListPadding.bottom() + st::boxPadding.bottom());
setDimensions(st::langsWidth, st::boxOptionListPadding.top() + count * _options.back()->heightNoMargins() + (count - 1) * st::boxOptionListSkip + st::boxOptionListPadding.bottom() + st::boxPadding.bottom());
}
void AutoLockBox::durationChanged(int seconds) {

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/background_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "window/themes/window_theme.h"
@@ -63,9 +63,9 @@ BackgroundBox::BackgroundBox(QWidget*) {
}
void BackgroundBox::prepare() {
setTitle(lang(lng_backgrounds_header));
setTitle(langFactory(lng_backgrounds_header));
addButton(lang(lng_close), [this] { closeBox(); });
addButton(langFactory(lng_close), [this] { closeBox(); });
setDimensions(st::boxWideWidth, st::boxMaxListHeight);

View File

@@ -55,7 +55,7 @@ attentionBoxButton: RoundButton(defaultBoxButton) {
defaultBoxCheckbox: Checkbox(defaultCheckbox) {
width: -46px;
textPosition: point(34px, 1px);
textPosition: point(12px, 1px);
style: boxTextStyle;
}
@@ -202,6 +202,10 @@ aboutRevokePublicLabel: FlatLabel(defaultFlatLabel) {
align: align(topleft);
width: 320px;
}
editBioCountdownLabel: FlatLabel(defaultFlatLabel) {
style: boxTextStyle;
textFg: windowSubTextFg;
}
contactUserIcon: icon {{ "add_contact_user", menuIconFg }};
contactPhoneIcon: icon {{ "add_contact_phone", menuIconFg }};
@@ -459,7 +463,7 @@ newGroupNamePosition: point(27px, 5px);
newGroupDescriptionPadding: margins(0px, 13px, 0px, 4px);
newGroupDescription: InputField(defaultInputField) {
textMargins: margins(1px, 26px, 1px, 4px);
heightMax: 135px;
heightMax: 116px;
}
setupChannelLink: InputField(defaultInputField) {
@@ -563,10 +567,7 @@ passcodeTextStyle: TextStyle(defaultTextStyle) {
usernamePadding: margins(23px, 6px, 21px, 12px);
usernameSkip: 49px;
usernameTextStyle: TextStyle(passcodeTextStyle) {
font: boxTextFont;
linkFont: boxTextFont;
linkFontOver: font(boxFontSize underline);
usernameTextStyle: TextStyle(boxTextStyle, passcodeTextStyle) {
}
usernameDefaultFg: windowSubTextFg;
@@ -631,3 +632,55 @@ changePhoneLabel: FlatLabel(defaultFlatLabel) {
changePhoneError: FlatLabel(changePhoneLabel) {
textFg: boxTextFgError;
}
adminLogFilterUserpicLeft: 15px;
adminLogFilterLittleSkip: 16px;
adminLogFilterCheckbox: Checkbox(defaultBoxCheckbox) {
style: TextStyle(boxTextStyle) {
font: font(boxFontSize semibold);
linkFont: font(boxFontSize semibold);
linkFontOver: font(boxFontSize semibold underline);
}
}
adminLogFilterSkip: 32px;
adminLogFilterUserCheckbox: Checkbox(defaultBoxCheckbox) {
margin: margins(8px, 6px, 8px, 6px);
}
rightsCheckbox: Checkbox(defaultBoxCheckbox) {
rippleBg: attentionButtonBgOver;
}
rightsToggle: Toggle(defaultToggle) {
toggledFg: windowBgActive;
untoggledFg: attentionButtonFg;
xsize: 8px;
vsize: 5px;
vshift: 1px;
stroke: 2px;
duration: 120;
}
rightsDividerHeight: 11px;
rightsHeaderMargin: margins(23px, 20px, 23px, 8px);
rightsToggleMargin: margins(23px, 8px, 23px, 8px);
rightsAboutMargin: margins(23px, 8px, 23px, 8px);
rightsPhotoButton: PeerAvatarButton {
size: 60px;
photoSize: 60px;
}
rightsPhotoMargin: margins(20px, 0px, 15px, 18px);
rightsNameStyle: TextStyle(semiboldTextStyle) {
font: font(15px semibold);
linkFont: font(15px semibold);
linkFontOver: font(15px semibold underline);
}
rightsNameTop: 8px;
rightsStatusTop: 32px;
rightsHeaderLabel: FlatLabel(boxLabel) {
style: TextStyle(semiboldTextStyle) {
font: font(boxFontSize semibold);
linkFont: font(boxFontSize semibold);
linkFontOver: font(boxFontSize semibold underline);
}
textFg: windowActiveTextFg;
}
rightsUntilMargin: margins(0px, 8px, 0px, 0px);

View File

@@ -23,7 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/buttons.h"
#include "styles/style_boxes.h"
#include "styles/style_dialogs.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "ui/effects/ripple_animation.h"
namespace {
@@ -463,7 +463,7 @@ void CalendarBox::prepare() {
// _inner = setInnerWidget(object_ptr<Inner>(this, _context.get()), st::calendarScroll, st::calendarTitleHeight);
_inner->setDateChosenCallback(std::move(_callback));
addButton(lang(lng_close), [this] { closeBox(); });
addButton(langFactory(lng_close), [this] { closeBox(); });
subscribe(_context->month(), [this](QDate month) { monthChanged(month); });

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/change_phone_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "styles/style_boxes.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/input_fields.h"
@@ -113,10 +113,10 @@ private:
};
void ChangePhoneBox::EnterPhone::prepare() {
setTitle(lang(lng_change_phone_title));
setTitle(langFactory(lng_change_phone_title));
auto phoneValue = QString();
_phone.create(this, st::defaultInputField, lang(lng_change_phone_new_title), phoneValue);
_phone.create(this, st::defaultInputField, langFactory(lng_change_phone_new_title), phoneValue);
_phone->resize(st::boxWidth - 2 * st::boxPadding.left(), _phone->height());
_phone->moveToLeft(st::boxPadding.left(), st::boxLittleSkip);
@@ -128,8 +128,8 @@ void ChangePhoneBox::EnterPhone::prepare() {
setDimensions(st::boxWidth, description->bottomNoMargins() + st::boxLittleSkip);
addButton(lang(lng_change_phone_new_submit), [this] { submit(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_change_phone_new_submit), [this] { submit(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
}
void ChangePhoneBox::EnterPhone::submit() {
@@ -206,14 +206,14 @@ ChangePhoneBox::EnterCode::EnterCode(QWidget*, const QString &phone, const QStri
}
void ChangePhoneBox::EnterCode::prepare() {
setTitle(lang(lng_change_phone_title));
setTitle(langFactory(lng_change_phone_title));
auto descriptionText = lng_change_phone_code_description(lt_phone, textcmdStartSemibold() + App::formatPhone(_phone) + textcmdStopSemibold());
auto description = object_ptr<Ui::FlatLabel>(this, descriptionText, Ui::FlatLabel::InitType::Rich, st::changePhoneLabel);
description->moveToLeft(st::boxPadding.left(), 0);
auto phoneValue = QString();
_code.create(this, st::defaultInputField, lang(lng_change_phone_code_title), phoneValue);
_code.create(this, st::defaultInputField, langFactory(lng_change_phone_code_title), phoneValue);
_code->setAutoSubmit(_codeLength, [this] { submit(); });
_code->setChangedCallback([this] { hideError(); });
@@ -228,8 +228,8 @@ void ChangePhoneBox::EnterCode::prepare() {
updateCall();
}
addButton(lang(lng_change_phone_new_submit), [this] { submit(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_change_phone_new_submit), [this] { submit(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
}
int ChangePhoneBox::EnterCode::countHeight() {
@@ -302,13 +302,13 @@ bool ChangePhoneBox::EnterCode::sendCodeFail(const RPCError &error) {
}
void ChangePhoneBox::prepare() {
setTitle(lang(lng_change_phone_title));
addButton(lang(lng_change_phone_button), [] {
setTitle(langFactory(lng_change_phone_title));
addButton(langFactory(lng_change_phone_button), [] {
Ui::show(Box<ConfirmBox>(lang(lng_change_phone_warning), [] {
Ui::show(Box<EnterPhone>());
}));
});
addButton(lang(lng_cancel), [this] {
addButton(langFactory(lng_cancel), [this] {
closeBox();
});

View File

@@ -21,7 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "boxes/confirm_box.h"
#include "styles/style_boxes.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "apiwrap.h"
@@ -33,6 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "core/click_handler_types.h"
#include "storage/localstorage.h"
#include "auth_session.h"
#include "observer_peer.h"
TextParseOptions _confirmBoxTextOptions = {
TextParseLinks | TextParseMultiline | TextParseRichText, // flags
@@ -115,9 +116,9 @@ void ConfirmBox::init(const QString &text) {
}
void ConfirmBox::prepare() {
addButton(_confirmText, [this] { confirmed(); }, _confirmStyle);
addButton([this] { return _confirmText; }, [this] { confirmed(); }, _confirmStyle);
if (!_informative) {
addButton(_cancelText, [this] { _cancelled = true; closeBox(); });
addButton([this] { return _cancelText; }, [this] { _cancelled = true; closeBox(); });
}
textUpdated();
}
@@ -186,9 +187,8 @@ void ConfirmBox::updateLink() {
}
void ConfirmBox::updateHover() {
QPoint m(mapFromGlobal(_lastMousePos));
auto state = _text.getStateLeft(m.x() - st::boxPadding.left(), m.y() - st::boxPadding.top(), _textWidth, width());
auto m = mapFromGlobal(_lastMousePos);
auto state = _text.getStateLeft(m - QPoint(st::boxPadding.left(), st::boxPadding.top()), _textWidth, width());
ClickHandler::setActive(state.link, this);
}
@@ -217,19 +217,25 @@ InformBox::InformBox(QWidget*, const QString &text, base::lambda<void()> closedC
InformBox::InformBox(QWidget*, const QString &text, const QString &doneText, base::lambda<void()> closedCallback) : ConfirmBox(ConfirmBox::InformBoxTag(), text, doneText, std::move(closedCallback)) {
}
MaxInviteBox::MaxInviteBox(QWidget*, const QString &link)
: _text(st::boxLabelStyle, lng_participant_invite_sorry(lt_count, Global::ChatSizeMax()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right())
, _link(link) {
MaxInviteBox::MaxInviteBox(QWidget*, gsl::not_null<ChannelData*> channel) : BoxContent()
, _channel(channel)
, _text(st::boxLabelStyle, lng_participant_invite_sorry(lt_count, Global::ChatSizeMax()), _confirmBoxTextOptions, st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right()) {
}
void MaxInviteBox::prepare() {
setMouseTracking(true);
addButton(lang(lng_box_ok), [this] { closeBox(); });
addButton(langFactory(lng_box_ok), [this] { closeBox(); });
_textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right();
_textHeight = qMin(_text.countHeight(_textWidth), 16 * st::boxLabelStyle.lineHeight);
setDimensions(st::boxWidth, st::boxPadding.top() + _textHeight + st::boxTextFont->height + st::boxTextFont->height * 2 + st::newGroupLinkPadding.bottom());
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::InviteLinkChanged, [this](const Notify::PeerUpdate &update) {
if (update.peer == _channel) {
rtlupdate(_invitationLink);
}
}));
}
void MaxInviteBox::mouseMoveEvent(QMouseEvent *e) {
@@ -239,8 +245,12 @@ void MaxInviteBox::mouseMoveEvent(QMouseEvent *e) {
void MaxInviteBox::mousePressEvent(QMouseEvent *e) {
mouseMoveEvent(e);
if (_linkOver) {
Application::clipboard()->setText(_link);
Ui::Toast::Show(lang(lng_create_channel_link_copied));
if (_channel->inviteLink().isEmpty()) {
App::api()->exportInviteLink(_channel);
} else {
QGuiApplication::clipboard()->setText(_channel->inviteLink());
Ui::Toast::Show(lang(lng_create_channel_link_copied));
}
}
}
@@ -272,7 +282,8 @@ void MaxInviteBox::paintEvent(QPaintEvent *e) {
option.setWrapMode(QTextOption::WrapAnywhere);
p.setFont(_linkOver ? st::defaultInputField.font->underline() : st::defaultInputField.font);
p.setPen(st::defaultLinkButton.color);
p.drawText(_invitationLink, _link, option);
auto inviteLinkText = _channel->inviteLink().isEmpty() ? lang(lng_group_invite_create) : _channel->inviteLink();
p.drawText(_invitationLink, inviteLinkText, option);
}
void MaxInviteBox::resizeEvent(QResizeEvent *e) {
@@ -293,10 +304,10 @@ void ConvertToSupergroupBox::prepare() {
text.push_back(lang(lng_profile_convert_feature3));
text.push_back(lang(lng_profile_convert_feature4));
setTitle(lang(lng_profile_convert_title));
setTitle(langFactory(lng_profile_convert_title));
addButton(lang(lng_profile_convert_confirm), [this] { convertToSupergroup(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_profile_convert_confirm), [this] { convertToSupergroup(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
_text.setText(st::boxLabelStyle, text.join('\n'), _confirmBoxTextOptions);
_note.setText(st::boxLabelStyle, lng_profile_convert_warning(lt_bold_start, textcmdStartSemibold(), lt_bold_end, textcmdStopSemibold()), _confirmBoxTextOptions);
@@ -363,8 +374,8 @@ PinMessageBox::PinMessageBox(QWidget*, ChannelData *channel, MsgId msgId)
}
void PinMessageBox::prepare() {
addButton(lang(lng_pinned_pin), [this] { pinMessage(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_pinned_pin), [this] { pinMessage(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
setDimensions(st::boxWidth, st::boxPadding.top() + _text->height() + st::boxMediumSkip + _notify->heightNoMargins() + st::boxPadding.bottom());
}
@@ -408,9 +419,13 @@ bool PinMessageBox::pinFail(const RPCError &error) {
DeleteMessagesBox::DeleteMessagesBox(QWidget*, HistoryItem *item, bool suggestModerateActions) : _singleItem(true) {
_ids.push_back(item->fullId());
if (suggestModerateActions && item->suggestBanReportDeleteAll()) {
_moderateFrom = item->from()->asUser();
_moderateInChannel = item->history()->peer->asChannel();
if (suggestModerateActions) {
_moderateBan = item->suggestBanReport();
_moderateDeleteAll = item->suggestDeleteAllReport();
if (_moderateBan || _moderateDeleteAll) {
_moderateFrom = item->from()->asUser();
_moderateInChannel = item->history()->peer->asChannel();
}
}
}
@@ -428,9 +443,13 @@ void DeleteMessagesBox::prepare() {
if (_moderateFrom) {
t_assert(_moderateInChannel != nullptr);
text = lang(lng_selected_delete_sure_this);
_banUser.create(this, lang(lng_ban_user), false, st::defaultBoxCheckbox);
if (_moderateBan) {
_banUser.create(this, lang(lng_ban_user), false, st::defaultBoxCheckbox);
}
_reportSpam.create(this, lang(lng_report_spam), false, st::defaultBoxCheckbox);
_deleteAll.create(this, lang(lng_delete_all_from), false, st::defaultBoxCheckbox);
if (_moderateDeleteAll) {
_deleteAll.create(this, lang(lng_delete_all_from), false, st::defaultBoxCheckbox);
}
} else {
text = _singleItem ? lang(lng_selected_delete_sure_this) : lng_selected_delete_sure(lt_count, _ids.size());
auto canDeleteAllForEveryone = true;
@@ -471,12 +490,19 @@ void DeleteMessagesBox::prepare() {
}
_text.create(this, text, Ui::FlatLabel::InitType::Simple, st::boxLabel);
addButton(lang(lng_box_delete), [this] { deleteAndClear(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_box_delete), [this] { deleteAndClear(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
auto fullHeight = st::boxPadding.top() + _text->height() + st::boxPadding.bottom();
if (_moderateFrom) {
fullHeight += st::boxMediumSkip + _banUser->heightNoMargins() + st::boxLittleSkip + _reportSpam->heightNoMargins() + st::boxLittleSkip + _deleteAll->heightNoMargins();
fullHeight += st::boxMediumSkip;
if (_banUser) {
fullHeight += _banUser->heightNoMargins() + st::boxLittleSkip;
}
fullHeight += _reportSpam->heightNoMargins();
if (_deleteAll) {
fullHeight += st::boxLittleSkip + _deleteAll->heightNoMargins();
}
} else if (_forEveryone) {
fullHeight += st::boxMediumSkip + _forEveryone->heightNoMargins();
}
@@ -487,9 +513,16 @@ void DeleteMessagesBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_text->moveToLeft(st::boxPadding.left(), st::boxPadding.top());
if (_moderateFrom) {
_banUser->moveToLeft(st::boxPadding.left(), _text->bottomNoMargins() + st::boxMediumSkip);
_reportSpam->moveToLeft(st::boxPadding.left(), _banUser->bottomNoMargins() + st::boxLittleSkip);
_deleteAll->moveToLeft(st::boxPadding.left(), _reportSpam->bottomNoMargins() + st::boxLittleSkip);
auto top = _text->bottomNoMargins() + st::boxMediumSkip;
if (_banUser) {
_banUser->moveToLeft(st::boxPadding.left(), top);
top += _banUser->heightNoMargins() + st::boxLittleSkip;
}
_reportSpam->moveToLeft(st::boxPadding.left(), top);
top += _reportSpam->heightNoMargins() + st::boxLittleSkip;
if (_deleteAll) {
_deleteAll->moveToLeft(st::boxPadding.left(), top);
}
} else if (_forEveryone) {
_forEveryone->moveToLeft(st::boxPadding.left(), _text->bottomNoMargins() + st::boxMediumSkip);
}
@@ -509,13 +542,13 @@ void DeleteMessagesBox::deleteAndClear() {
}
if (_moderateFrom) {
if (_banUser->checked()) {
MTP::send(MTPchannels_KickFromChannel(_moderateInChannel->inputChannel, _moderateFrom->inputUser, MTP_boolTrue()), App::main()->rpcDone(&MainWidget::sentUpdatesReceived));
if (_banUser && _banUser->checked()) {
App::api()->kickParticipant(_moderateInChannel, _moderateFrom, MTP_channelBannedRights(MTP_flags(0), MTP_int(0)));
}
if (_reportSpam->checked()) {
MTP::send(MTPchannels_ReportSpam(_moderateInChannel->inputChannel, _moderateFrom->inputUser, MTP_vector<MTPint>(1, MTP_int(_ids[0].msg))));
}
if (_deleteAll->checked()) {
if (_deleteAll && _deleteAll->checked()) {
App::main()->deleteAllFromUser(_moderateInChannel, _moderateFrom);
}
}
@@ -547,14 +580,18 @@ void DeleteMessagesBox::deleteAndClear() {
Ui::hideLayer();
}
ConfirmInviteBox::ConfirmInviteBox(QWidget*, const QString &title, const MTPChatPhoto &photo, int count, const QVector<UserData*> &participants)
ConfirmInviteBox::ConfirmInviteBox(QWidget*, const QString &title, bool isChannel, const MTPChatPhoto &photo, int count, const QVector<UserData*> &participants)
: _title(this, st::confirmInviteTitle)
, _status(this, st::confirmInviteStatus)
, _participants(participants) {
_title->setText(title);
QString status;
if (_participants.isEmpty() || _participants.size() >= count) {
status = lng_chat_status_members(lt_count, count);
if (count > 0) {
status = lng_chat_status_members(lt_count, count);
} else {
status = lang(isChannel ? lng_channel_status : lng_group_status);
}
} else {
status = lng_group_invite_members(lt_count, count);
}
@@ -576,12 +613,12 @@ ConfirmInviteBox::ConfirmInviteBox(QWidget*, const QString &title, const MTPChat
}
void ConfirmInviteBox::prepare() {
addButton(lang(lng_group_invite_join), [this] {
addButton(langFactory(lng_group_invite_join), [this] {
if (auto main = App::main()) {
main->onInviteImport();
}
});
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
if (_participants.size() > 4) {
_participants.resize(4);

View File

@@ -100,7 +100,7 @@ public:
class MaxInviteBox : public BoxContent {
public:
MaxInviteBox(QWidget*, const QString &link);
MaxInviteBox(QWidget*, gsl::not_null<ChannelData*> channel);
protected:
void prepare() override;
@@ -114,10 +114,11 @@ protected:
private:
void updateSelected(const QPoint &cursorGlobalPosition);
gsl::not_null<ChannelData*> _channel;
Text _text;
int32 _textWidth, _textHeight;
QString _link;
QRect _invitationLink;
bool _linkOver = false;
@@ -189,6 +190,8 @@ private:
bool _singleItem = false;
UserData *_moderateFrom = nullptr;
ChannelData *_moderateInChannel = nullptr;
bool _moderateBan = false;
bool _moderateDeleteAll = false;
object_ptr<Ui::FlatLabel> _text = { nullptr };
object_ptr<Ui::Checkbox> _forEveryone = { nullptr };
@@ -200,7 +203,7 @@ private:
class ConfirmInviteBox : public BoxContent, public RPCSender {
public:
ConfirmInviteBox(QWidget*, const QString &title, const MTPChatPhoto &photo, int count, const QVector<UserData*> &participants);
ConfirmInviteBox(QWidget*, const QString &title, bool isChannel, const MTPChatPhoto &photo, int count, const QVector<UserData*> &participants);
protected:
void prepare() override;

View File

@@ -26,7 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
#include "mainwidget.h"
#include "lang.h"
#include "lang/lang_keys.h"
namespace {
@@ -210,14 +210,14 @@ void ConfirmPhoneBox::prepare() {
}
_about->setMarkedText(aboutText);
_code.create(this, st::confirmPhoneCodeField, lang(lng_code_ph));
_code.create(this, st::confirmPhoneCodeField, langFactory(lng_code_ph));
_code->setAutoSubmit(_sentCodeLength, [this] { onSendCode(); });
_code->setChangedCallback([this] { showError(QString()); });
setTitle(lang(lng_confirm_phone_title));
setTitle(langFactory(lng_confirm_phone_title));
addButton(lang(lng_confirm_phone_send), [this] { onSendCode(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_confirm_phone_send), [this] { onSendCode(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
setDimensions(st::boxWidth, st::usernamePadding.top() + _code->height() + st::usernameSkip + _about->height() + st::usernameSkip);

View File

@@ -30,7 +30,7 @@ class FlatLabel;
class SentCodeField : public Ui::InputField {
public:
SentCodeField(QWidget *parent, const style::InputField &st, const QString &ph = QString(), const QString &val = QString()) : Ui::InputField(parent, st, ph, val) {
SentCodeField(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory = base::lambda<QString()>(), const QString &val = QString()) : Ui::InputField(parent, st, std::move(placeholderFactory), val) {
connect(this, &Ui::InputField::changed, [this] { fix(); });
}

View File

@@ -20,7 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/connection_box.h"
#include "lang.h"
#include "boxes/confirm_box.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "mainwidget.h"
#include "mainwindow.h"
@@ -30,11 +31,36 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "history/history_location_manager.h"
#include "styles/style_boxes.h"
void ConnectionBox::ShowApplyProxyConfirmation(const QMap<QString, QString> &fields) {
auto server = fields.value(qsl("server"));
auto port = fields.value(qsl("port")).toInt();
if (!server.isEmpty() && port != 0) {
auto weakBox = std::make_shared<QPointer<ConfirmBox>>(nullptr);
auto box = Ui::show(Box<ConfirmBox>(lng_sure_enable_socks(lt_server, server, lt_port, QString::number(port)), lang(lng_sure_enable), [fields, weakBox] {
auto p = ProxyData();
p.host = fields.value(qsl("server"));
p.user = fields.value(qsl("user"));
p.password = fields.value(qsl("pass"));
p.port = fields.value(qsl("port")).toInt();
Global::SetConnectionType(dbictTcpProxy);
Global::SetLastProxyType(dbictTcpProxy);
Global::SetConnectionProxy(p);
Local::writeSettings();
Global::RefConnectionTypeChanged().notify();
MTP::restart();
reinitLocationManager();
reinitWebLoadManager();
if (*weakBox) (*weakBox)->closeBox();
}), KeepOtherLayers);
*weakBox = box;
}
}
ConnectionBox::ConnectionBox(QWidget *parent)
: _hostInput(this, st::connectionHostInputField, lang(lng_connection_host_ph), Global::ConnectionProxy().host)
, _portInput(this, st::connectionPortInputField, lang(lng_connection_port_ph), QString::number(Global::ConnectionProxy().port))
, _userInput(this, st::connectionUserInputField, lang(lng_connection_user_ph), Global::ConnectionProxy().user)
, _passwordInput(this, st::connectionPasswordInputField, lang(lng_connection_password_ph), Global::ConnectionProxy().password)
: _hostInput(this, st::connectionHostInputField, langFactory(lng_connection_host_ph), Global::ConnectionProxy().host)
, _portInput(this, st::connectionPortInputField, langFactory(lng_connection_port_ph), QString::number(Global::ConnectionProxy().port))
, _userInput(this, st::connectionUserInputField, langFactory(lng_connection_user_ph), Global::ConnectionProxy().user)
, _passwordInput(this, st::connectionPasswordInputField, langFactory(lng_connection_password_ph), Global::ConnectionProxy().password)
, _typeGroup(std::make_shared<Ui::RadioenumGroup<DBIConnectionType>>(Global::ConnectionType()))
, _autoRadio(this, _typeGroup, dbictAuto, lang(lng_connection_auto_rb), st::defaultBoxCheckbox)
, _httpProxyRadio(this, _typeGroup, dbictHttpProxy, lang(lng_connection_http_proxy_rb), st::defaultBoxCheckbox)
@@ -43,10 +69,10 @@ ConnectionBox::ConnectionBox(QWidget *parent)
}
void ConnectionBox::prepare() {
setTitle(lang(lng_connection_header));
setTitle(langFactory(lng_connection_header));
addButton(lang(lng_connection_save), [this] { onSave(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_connection_save), [this] { onSave(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
_typeGroup->setChangedCallback([this](DBIConnectionType value) { typeChanged(value); });
@@ -54,13 +80,21 @@ void ConnectionBox::prepare() {
connect(_portInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
connect(_userInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
connect(_passwordInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
connect(_hostInput, SIGNAL(focused()), this, SLOT(onFieldFocus()));
connect(_portInput, SIGNAL(focused()), this, SLOT(onFieldFocus()));
connect(_userInput, SIGNAL(focused()), this, SLOT(onFieldFocus()));
connect(_passwordInput, SIGNAL(focused()), this, SLOT(onFieldFocus()));
updateControlsVisibility();
}
bool ConnectionBox::badProxyValue() const {
return (_hostInput->getLastText().isEmpty() || !_portInput->getLastText().toInt());
}
void ConnectionBox::updateControlsVisibility() {
auto newHeight = st::boxOptionListPadding.top() + _autoRadio->heightNoMargins() + st::boxOptionListSkip + _httpProxyRadio->heightNoMargins() + st::boxOptionListSkip + _tcpProxyRadio->heightNoMargins() + st::boxOptionListSkip + st::connectionIPv6Skip + _tryIPv6->heightNoMargins() + st::boxOptionListPadding.bottom() + st::boxPadding.bottom();
if (_typeGroup->value() == dbictAuto) {
if (_typeGroup->value() == dbictAuto && badProxyValue()) {
_hostInput->hide();
_portInput->hide();
_userInput->hide();
@@ -78,7 +112,7 @@ void ConnectionBox::updateControlsVisibility() {
}
void ConnectionBox::setInnerFocus() {
if (_hostInput->isHidden()) {
if (_typeGroup->value() == dbictAuto) {
setFocus();
} else {
_hostInput->setFocusFast();
@@ -93,34 +127,41 @@ void ConnectionBox::resizeEvent(QResizeEvent *e) {
void ConnectionBox::updateControlsPosition() {
auto type = _typeGroup->value();
_autoRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxOptionListPadding.top());
_autoRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _autoRadio->getMargins().top() + st::boxOptionListPadding.top());
_httpProxyRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _autoRadio->bottomNoMargins() + st::boxOptionListSkip);
auto inputy = 0;
if (type == dbictHttpProxy) {
auto fieldsVisible = (type != dbictAuto) || (!badProxyValue() && Global::LastProxyType() != dbictAuto);
auto fieldsBelowHttp = fieldsVisible && (type == dbictHttpProxy || (type == dbictAuto && Global::LastProxyType() == dbictHttpProxy));
auto fieldsBelowTcp = fieldsVisible && (type == dbictTcpProxy || (type == dbictAuto && Global::LastProxyType() == dbictTcpProxy));
if (fieldsBelowHttp) {
inputy = _httpProxyRadio->bottomNoMargins() + st::boxOptionInputSkip;
_tcpProxyRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), inputy + st::boxOptionInputSkip + 2 * _hostInput->height() + st::boxOptionListSkip);
} else {
_tcpProxyRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _httpProxyRadio->bottomNoMargins() + st::boxOptionListSkip);
if (type == dbictTcpProxy) {
if (fieldsBelowTcp) {
inputy = _tcpProxyRadio->bottomNoMargins() + st::boxOptionInputSkip;
}
}
if (inputy) {
_hostInput->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultBoxCheckbox.textPosition.x() - st::defaultInputField.textMargins.left(), inputy);
_hostInput->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultCheck.diameter + st::defaultBoxCheckbox.textPosition.x() - st::defaultInputField.textMargins.left(), inputy);
_portInput->moveToRight(st::boxPadding.right(), inputy);
_userInput->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultBoxCheckbox.textPosition.x() - st::defaultInputField.textMargins.left(), _hostInput->y() + _hostInput->height() + st::boxOptionInputSkip);
_userInput->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultCheck.diameter + st::defaultBoxCheckbox.textPosition.x() - st::defaultInputField.textMargins.left(), _hostInput->y() + _hostInput->height() + st::boxOptionInputSkip);
_passwordInput->moveToRight(st::boxPadding.right(), _userInput->y());
}
auto tryipv6y = ((type == dbictTcpProxy) ? _userInput->bottomNoMargins() : _tcpProxyRadio->bottomNoMargins()) + st::boxOptionListSkip + st::connectionIPv6Skip;
auto tryipv6y = (fieldsBelowTcp ? _userInput->bottomNoMargins() : _tcpProxyRadio->bottomNoMargins()) + st::boxOptionListSkip + st::connectionIPv6Skip;
_tryIPv6->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), tryipv6y);
}
void ConnectionBox::typeChanged(DBIConnectionType type) {
if (type == dbictAuto) {
setFocus();
}
updateControlsVisibility();
if (type != dbictAuto) {
Global::SetLastProxyType(type);
if (!_hostInput->hasFocus() && !_portInput->hasFocus() && !_userInput->hasFocus() && !_passwordInput->hasFocus()) {
_hostInput->setFocusFast();
}
@@ -132,7 +173,16 @@ void ConnectionBox::typeChanged(DBIConnectionType type) {
update();
}
void ConnectionBox::onFieldFocus() {
if (Global::LastProxyType() == dbictHttpProxy) {
_typeGroup->setValue(dbictHttpProxy);
} else if (Global::LastProxyType() == dbictTcpProxy) {
_typeGroup->setValue(dbictTcpProxy);
}
}
void ConnectionBox::onSubmit() {
onFieldFocus();
if (_hostInput->hasFocus()) {
if (!_hostInput->getLastText().trimmed().isEmpty()) {
_portInput->setFocus();
@@ -161,30 +211,33 @@ void ConnectionBox::onSubmit() {
}
void ConnectionBox::onSave() {
auto p = ProxyData();
p.host = _hostInput->getLastText().trimmed();
p.user = _userInput->getLastText().trimmed();
p.password = _passwordInput->getLastText().trimmed();
p.port = _portInput->getLastText().toInt();
auto type = _typeGroup->value();
if (type == dbictAuto) {
Global::SetConnectionType(type);
Global::SetConnectionProxy(ProxyData());
if (p.host.isEmpty() || !p.port) {
p = ProxyData();
}
#ifndef TDESKTOP_DISABLE_NETWORK_PROXY
QNetworkProxyFactory::setUseSystemConfiguration(false);
QNetworkProxyFactory::setUseSystemConfiguration(true);
#endif // !TDESKTOP_DISABLE_NETWORK_PROXY
} else {
ProxyData p;
p.host = _hostInput->getLastText().trimmed();
p.user = _userInput->getLastText().trimmed();
p.password = _passwordInput->getLastText().trimmed();
p.port = _portInput->getLastText().toInt();
if (p.host.isEmpty()) {
_hostInput->setFocus();
_hostInput->showError();
return;
} else if (!p.port) {
_portInput->setFocus();
_portInput->showError();
return;
}
Global::SetConnectionType(type);
Global::SetConnectionProxy(p);
Global::SetLastProxyType(type);
}
Global::SetConnectionType(type);
Global::SetConnectionProxy(p);
if (cPlatform() == dbipWindows && Global::TryIPv6() != _tryIPv6->checked()) {
Global::SetTryIPv6(_tryIPv6->checked());
Local::writeSettings();
@@ -211,12 +264,12 @@ AutoDownloadBox::AutoDownloadBox(QWidget *parent)
, _gifPrivate(this, lang(lng_media_auto_private_chats), !(cAutoDownloadGif() & dbiadNoPrivate), st::defaultBoxCheckbox)
, _gifGroups(this, lang(lng_media_auto_groups), !(cAutoDownloadGif() & dbiadNoGroups), st::defaultBoxCheckbox)
, _gifPlay(this, lang(lng_media_auto_play), cAutoPlayGif(), st::defaultBoxCheckbox)
, _sectionHeight(st::boxTitleHeight + 2 * (st::defaultBoxCheckbox.height + st::setLittleSkip)) {
, _sectionHeight(st::boxTitleHeight + 2 * (st::defaultCheck.diameter + st::setLittleSkip)) {
}
void AutoDownloadBox::prepare() {
addButton(lang(lng_connection_save), [this] { onSave(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_connection_save), [this] { onSave(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
setDimensions(st::boxWidth, 3 * _sectionHeight - st::autoDownloadTopDelta + st::setLittleSkip + _gifPlay->heightNoMargins() + st::setLittleSkip);
}

View File

@@ -39,6 +39,8 @@ class ConnectionBox : public BoxContent {
public:
ConnectionBox(QWidget *parent);
static void ShowApplyProxyConfirmation(const QMap<QString, QString> &fields);
protected:
void prepare() override;
void setInnerFocus() override;
@@ -47,12 +49,14 @@ protected:
private slots:
void onSubmit();
void onFieldFocus();
void onSave();
private:
void typeChanged(DBIConnectionType type);
void updateControlsVisibility();
void updateControlsPosition();
bool badProxyValue() const;
object_ptr<Ui::InputField> _hostInput;
object_ptr<Ui::PortInput> _portInput;

View File

@@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_dialogs.h"
#include "styles/style_history.h"
#include "styles/style_profile.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "boxes/add_contact_box.h"
#include "mainwidget.h"
#include "mainwindow.h"
@@ -39,12 +39,41 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/effects/ripple_animation.h"
#include "boxes/photo_crop_box.h"
#include "boxes/confirm_box.h"
#include "boxes/edit_participant_box.h"
#include "window/themes/window_theme.h"
#include "observer_peer.h"
#include "apiwrap.h"
#include "auth_session.h"
#include "storage/file_download.h"
// Not used for now.
//
//MembersAddButton::MembersAddButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple)
//, _st(st) {
// resize(_st.width, _st.height);
// setCursor(style::cur_pointer);
//}
//
//void MembersAddButton::paintEvent(QPaintEvent *e) {
// Painter p(this);
//
// auto ms = getms();
// auto over = isOver();
// auto down = isDown();
//
// ((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width());
// paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms);
// ((over || down) ? _st.iconAboveOver : _st.iconAbove).paint(p, _st.iconPosition, width());
//}
//
//QImage MembersAddButton::prepareRippleMask() const {
// return Ui::RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize));
//}
//
//QPoint MembersAddButton::prepareRippleStartPosition() const {
// return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition;
//}
QString PeerFloodErrorText(PeerFloodType type) {
auto link = textcmdLink(Messenger::Instance().createInternalLinkFull(qsl("spambot")), lang(lng_cant_more_info));
if (type == PeerFloodType::InviteGroup) {
@@ -113,24 +142,24 @@ void ContactsBox::prepare() {
updateTitle();
if (_chat) {
if (_membersFilter == MembersFilter::Admins) {
addButton(lang(lng_settings_save), [this] { saveChatAdmins(); });
addButton(langFactory(lng_settings_save), [this] { saveChatAdmins(); });
} else {
addButton(lang(lng_participant_invite), [this] { inviteParticipants(); });
addButton(langFactory(lng_participant_invite), [this] { inviteParticipants(); });
}
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
} else if (_channel) {
if (_membersFilter != MembersFilter::Admins) {
addButton(lang(lng_participant_invite), [this] { inviteParticipants(); });
addButton(langFactory(lng_participant_invite), [this] { inviteParticipants(); });
}
addButton(lang((_creating == CreatingGroupChannel) ? lng_create_group_skip : lng_cancel), [this] { closeBox(); });
addButton(langFactory((_creating == CreatingGroupChannel) ? lng_create_group_skip : lng_cancel), [this] { closeBox(); });
} else if (_bot) {
addButton(lang(lng_close), [this] { closeBox(); });
addButton(langFactory(lng_close), [this] { closeBox(); });
} else if (_creating == CreatingGroupGroup) {
addButton(lang(lng_create_group_create), [this] { createGroup(); });
addButton(lang(lng_create_group_back), [this] { closeBox(); });
addButton(langFactory(lng_create_group_create), [this] { createGroup(); });
addButton(langFactory(lng_create_group_back), [this] { closeBox(); });
} else {
addButton(lang(lng_close), [this] { closeBox(); });
addLeftButton(lang(lng_profile_add_contact), [] { App::wnd()->onShowAddContact(); });
addButton(langFactory(lng_close), [this] { closeBox(); });
addLeftButton(langFactory(lng_profile_add_contact), [] { App::wnd()->onShowAddContact(); });
}
_inner->setPeerSelectedChangedCallback([this](PeerData *peer, bool checked) {
@@ -199,19 +228,18 @@ bool ContactsBox::onSearchByUsername(bool searchCache) {
void ContactsBox::updateTitle() {
if (_chat && _membersFilter == MembersFilter::Admins) {
setTitle(lang(lng_channel_admins));
setTitle(langFactory(lng_channel_admins));
} else if (_chat || _creating != CreatingGroupNone) {
auto addingAdmin = _channel && (_membersFilter == MembersFilter::Admins);
auto title = lang(addingAdmin ? lng_channel_add_admin : lng_profile_add_participant);
auto additional = (addingAdmin || (_inner->channel() && !_inner->channel()->isMegagroup())) ? QString() : QString("%1 / %2").arg(_inner->selectedCount()).arg(Global::MegagroupSizeMax());
setTitle(title);
setAdditionalTitle(additional);
setTitle(langFactory(addingAdmin ? lng_channel_add_admin : lng_profile_add_participant));
setAdditionalTitle([additional] { return additional; });
} else if (_inner->sharingBotGame()) {
setTitle(lang(lng_bot_choose_chat));
setTitle(langFactory(lng_bot_choose_chat));
} else if (_inner->bot()) {
setTitle(lang(lng_bot_choose_group));
setTitle(langFactory(lng_bot_choose_group));
} else {
setTitle(lang(lng_contacts_header));
setTitle(langFactory(lng_contacts_header));
}
}
@@ -287,7 +315,7 @@ void ContactsBox::keyPressEvent(QKeyEvent *e) {
}
object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>> ContactsBox::createMultiSelect() {
auto entity = object_ptr<Ui::MultiSelect>(this, st::contactsMultiSelect, lang(lng_participant_filter));
auto entity = object_ptr<Ui::MultiSelect>(this, st::contactsMultiSelect, langFactory(lng_participant_filter));
auto margins = style::margins(0, 0, 0, 0);
auto callback = [this] { updateScrollSkips(); };
return object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>>(this, std::move(entity), margins, std::move(callback));
@@ -316,6 +344,13 @@ void ContactsBox::resizeEvent(QResizeEvent *e) {
_inner->resize(width(), _inner->height());
}
void ContactsBox::paintEvent(QPaintEvent *e) {
Painter p(this);
for (auto rect : e->region().rects()) {
p.fillRect(rect, st::contactsBg);
}
}
void ContactsBox::closeHook() {
if (_channel && _creating == CreatingGroupChannel) {
Ui::showPeerHistory(_channel, ShowAtTheEndMsgId);
@@ -344,8 +379,8 @@ void ContactsBox::onPeerSelectedChanged(PeerData *peer, bool checked) {
}
void ContactsBox::inviteParticipants() {
QVector<UserData*> users(_inner->selected());
if (users.isEmpty()) {
auto users = _inner->selected();
if (users.empty()) {
_select->entity()->setInnerFocus();
return;
}
@@ -363,7 +398,7 @@ void ContactsBox::createGroup() {
if (_saveRequestId) return;
auto users = _inner->selectedInputs();
if (users.isEmpty() || (users.size() == 1 && users.at(0).type() == mtpc_inputUserSelf)) {
if (users.empty() || (users.size() == 1 && users.at(0).type() == mtpc_inputUserSelf)) {
_select->entity()->setInnerFocus();
return;
}
@@ -396,15 +431,16 @@ void ContactsBox::getAdminsDone(const MTPmessages_ChatFull &result) {
closeBox();
return;
}
ChatData::Admins curadmins = _inner->chat()->admins;
QVector<UserData*> newadmins = _inner->selected(), appoint;
if (!newadmins.isEmpty()) {
auto curadmins = _inner->chat()->admins;
auto newadmins = _inner->selected();
auto appoint = decltype(newadmins)();
if (!newadmins.empty()) {
appoint.reserve(newadmins.size());
for (int32 i = 0, l = newadmins.size(); i < l; ++i) {
ChatData::Admins::iterator c = curadmins.find(newadmins.at(i));
for (auto &user : newadmins) {
auto c = curadmins.find(user);
if (c == curadmins.cend()) {
if (newadmins.at(i)->id != peerFromUser(_inner->chat()->creator)) {
appoint.push_back(newadmins.at(i));
if (user->id != peerFromUser(_inner->chat()->creator)) {
appoint.push_back(user);
}
} else {
curadmins.erase(c);
@@ -413,10 +449,10 @@ void ContactsBox::getAdminsDone(const MTPmessages_ChatFull &result) {
}
_saveRequestId = 0;
for_const (UserData *user, curadmins) {
for_const (auto user, curadmins) {
MTP::send(MTPmessages_EditChatAdmin(_inner->chat()->inputChat, user->inputUser, MTP_boolFalse()), rpcDone(&ContactsBox::removeAdminDone, user), rpcFail(&ContactsBox::editAdminFail), 0, 10);
}
for_const (UserData *user, appoint) {
for_const (auto user, appoint) {
MTP::send(MTPmessages_EditChatAdmin(_inner->chat()->inputChat, user->inputUser, MTP_boolTrue()), rpcDone(&ContactsBox::setAdminDone, user), rpcFail(&ContactsBox::editAdminFail), 0, 10);
}
MTP::sendAnything();
@@ -427,7 +463,7 @@ void ContactsBox::getAdminsDone(const MTPmessages_ChatFull &result) {
}
}
void ContactsBox::setAdminDone(UserData *user, const MTPBool &result) {
void ContactsBox::setAdminDone(gsl::not_null<UserData*> user, const MTPBool &result) {
if (mtpIsTrue(result)) {
if (_inner->chat()->noParticipantInfo()) {
App::api()->requestFullPeer(_inner->chat());
@@ -442,7 +478,7 @@ void ContactsBox::setAdminDone(UserData *user, const MTPBool &result) {
}
}
void ContactsBox::removeAdminDone(UserData *user, const MTPBool &result) {
void ContactsBox::removeAdminDone(gsl::not_null<UserData*> user, const MTPBool &result) {
if (mtpIsTrue(result)) {
_inner->chat()->admins.remove(user);
}
@@ -610,7 +646,7 @@ ContactsBox::Inner::Inner(QWidget *parent, UserData *bot) : TWidget(parent)
addDialogsToList([](PeerData *peer) {
if (peer->isChat() && peer->asChat()->canEdit()) {
return true;
} else if (peer->isMegagroup() && (peer->asChannel()->amCreator() || peer->asChannel()->amEditor())) {
} else if (peer->isMegagroup()) {
return true;
}
return false;
@@ -622,7 +658,7 @@ ContactsBox::Inner::Inner(QWidget *parent, UserData *bot) : TWidget(parent)
void ContactsBox::Inner::init() {
subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); });
connect(_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact()));
connect(_allAdmins, SIGNAL(changed()), this, SLOT(onAllAdminsChanged()));
subscribe(_allAdmins->checkedChanged, [this](bool checked) { onAllAdminsChanged(); });
_rowsTop = st::contactsMarginTop;
setAttribute(Qt::WA_OpaquePaintEvent);
@@ -719,10 +755,10 @@ void ContactsBox::Inner::addBot() {
} else if (!info->startGroupToken.isEmpty()) {
MTP::send(MTPmessages_StartBot(_bot->inputUser, _addToPeer->input, MTP_long(rand_value<uint64>()), MTP_string(info->startGroupToken)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, { _bot, _addToPeer }));
} else {
App::main()->addParticipants(_addToPeer, QVector<UserData*>(1, _bot));
App::main()->addParticipants(_addToPeer, std::vector<gsl::not_null<UserData*>>(1, _bot));
}
} else {
App::main()->addParticipants(_addToPeer, QVector<UserData*>(1, _bot));
App::main()->addParticipants(_addToPeer, std::vector<gsl::not_null<UserData*>>(1, _bot));
}
Ui::hideLayer();
Ui::showPeerHistory(_addToPeer, ShowAtUnreadMsgId);
@@ -737,50 +773,6 @@ void ContactsBox::Inner::onAllAdminsChanged() {
update();
}
void ContactsBox::Inner::addAdminDone(const MTPUpdates &result, mtpRequestId req) {
if (App::main()) App::main()->sentUpdatesReceived(result);
if (req != _addAdminRequestId) return;
_addAdminRequestId = 0;
if (_addAdmin && _channel && _channel->isMegagroup()) {
Notify::PeerUpdate update(_channel);
if (_channel->mgInfo->lastParticipants.indexOf(_addAdmin) < 0) {
_channel->mgInfo->lastParticipants.push_front(_addAdmin);
update.flags |= Notify::PeerUpdate::Flag::MembersChanged;
}
_channel->mgInfo->lastAdmins.insert(_addAdmin);
update.flags |= Notify::PeerUpdate::Flag::AdminsChanged;
if (_addAdmin->botInfo) {
_channel->mgInfo->bots.insert(_addAdmin);
if (_channel->mgInfo->botStatus != 0 && _channel->mgInfo->botStatus < 2) {
_channel->mgInfo->botStatus = 2;
}
}
Notify::peerUpdatedDelayed(update);
}
if (_addAdminBox) _addAdminBox->closeBox();
emit adminAdded();
}
bool ContactsBox::Inner::addAdminFail(const RPCError &error, mtpRequestId req) {
if (MTP::isDefaultHandledError(error)) return false;
if (req != _addAdminRequestId) return true;
_addAdminRequestId = 0;
if (_addAdminBox) _addAdminBox->closeBox();
if (error.type() == "USERS_TOO_MUCH") {
Ui::show(Box<MaxInviteBox>(_channel->inviteLink()), KeepOtherLayers);
} else if (error.type() == "ADMINS_TOO_MUCH") {
Ui::show(Box<InformBox>(lang(lng_channel_admins_too_much)), KeepOtherLayers);
} else if (error.type() == qstr("USER_RESTRICTED")) {
Ui::show(Box<InformBox>(lang(lng_cant_do_this)), KeepOtherLayers);
} else {
emit adminAdded();
}
return true;
}
void ContactsBox::Inner::saving(bool flag) {
_saving = flag;
_allAdminsChecked = _allAdmins->checked();
@@ -899,11 +891,13 @@ ContactsBox::Inner::ContactData *ContactsBox::Inner::contactData(Dialogs::Row *r
data->statusText = App::onlineText(peer->asUser(), _time);
data->statusHasOnlineColor = App::onlineColorUse(peer->asUser(), _time);
} else if (peer->isChat()) {
ChatData *chat = peer->asChat();
auto chat = peer->asChat();
if (!chat->amIn()) {
data->statusText = lang(lng_chat_status_unaccessible);
} else {
} else if (chat->count > 0) {
data->statusText = lng_chat_status_members(lt_count, chat->count);
} else {
data->statusText = lang(lng_group_status);
}
} else if (peer->isMegagroup()) {
data->statusText = lang(lng_group_status);
@@ -1241,9 +1235,12 @@ void ContactsBox::Inner::leaveEventHook(QEvent *e) {
}
void ContactsBox::Inner::mouseMoveEvent(QMouseEvent *e) {
_mouseSelection = true;
_lastMousePos = e->globalPos();
updateSelection();
auto position = e->globalPos();
if (_mouseSelection || _lastMousePos != position) {
_mouseSelection = true;
_lastMousePos = e->globalPos();
updateSelection();
}
}
void ContactsBox::Inner::mousePressEvent(QMouseEvent *e) {
@@ -1334,112 +1331,133 @@ void ContactsBox::Inner::setSearchedPressed(int pressed) {
_searchedPressed = pressed;
}
void ContactsBox::Inner::chooseParticipant() {
if (_saving) return;
bool addingAdmin = (_channel && _membersFilter == MembersFilter::Admins);
if (!addingAdmin && usingMultiSelect()) {
_time = unixtime();
if (_filter.isEmpty()) {
if (_searchedSelected >= 0 && _searchedSelected < _byUsername.size()) {
auto data = d_byUsername[_searchedSelected];
auto peer = _byUsername[_searchedSelected];
if (data->disabledChecked) return;
void ContactsBox::Inner::changeMultiSelectCheckState() {
_time = unixtime();
if (_filter.isEmpty()) {
if (_searchedSelected >= 0 && _searchedSelected < _byUsername.size()) {
auto data = d_byUsername[_searchedSelected];
auto peer = _byUsername[_searchedSelected];
if (data->disabledChecked) return;
changeCheckState(data, peer);
} else if (_selected) {
auto data = contactData(_selected);
auto peer = _selected->history()->peer;
if (data->disabledChecked) return;
changeCheckState(data, peer);
} else if (_selected) {
auto data = contactData(_selected);
auto peer = _selected->history()->peer;
if (data->disabledChecked) return;
changeCheckState(_selected);
}
} else {
if (_searchedSelected >= 0 && _searchedSelected < _byUsernameFiltered.size()) {
auto data = d_byUsernameFiltered[_searchedSelected];
auto peer = _byUsernameFiltered[_searchedSelected];
if (data->disabledChecked) return;
int i = 0, l = d_byUsername.size();
for (; i < l; ++i) {
if (d_byUsername[i] == data) {
break;
}
}
if (i == l) {
d_byUsername.push_back(data);
_byUsername.push_back(peer);
for (i = 0, l = _byUsernameDatas.size(); i < l;) {
if (_byUsernameDatas[i] == data) {
_byUsernameDatas.removeAt(i);
--l;
} else {
++i;
}
}
}
changeCheckState(data, peer);
} else if (_filteredSelected >= 0 && _filteredSelected < _filtered.size()) {
auto data = contactData(_filtered[_filteredSelected]);
auto peer = _filtered[_filteredSelected]->history()->peer;
if (data->disabledChecked) return;
changeCheckState(data, peer);
}
changeCheckState(_selected);
}
} else {
PeerData *peer = 0;
if (_filter.isEmpty()) {
if (_searchedSelected >= 0 && _searchedSelected < _byUsername.size()) {
peer = _byUsername[_searchedSelected];
} else if (_selected) {
peer = _selected->history()->peer;
}
} else {
if (_searchedSelected >= 0 && _searchedSelected < _byUsernameFiltered.size()) {
peer = _byUsernameFiltered[_searchedSelected];
} else {
if (_filteredSelected < 0 || _filteredSelected >= _filtered.size()) return;
peer = _filtered[_filteredSelected]->history()->peer;
}
}
if (peer) {
if (addingAdmin) {
_addAdmin = peer->asUser();
if (_addAdminRequestId) {
MTP::cancel(_addAdminRequestId);
_addAdminRequestId = 0;
if (_searchedSelected >= 0 && _searchedSelected < _byUsernameFiltered.size()) {
auto data = d_byUsernameFiltered[_searchedSelected];
auto peer = _byUsernameFiltered[_searchedSelected];
if (data->disabledChecked) return;
int i = 0, l = d_byUsername.size();
for (; i < l; ++i) {
if (d_byUsername[i] == data) {
break;
}
if (_addAdminBox) _addAdminBox->deleteLater();
_addAdminBox = Ui::show(Box<ConfirmBox>(lng_channel_admin_sure(lt_user, _addAdmin->firstName), base::lambda_guarded(this, [this] {
if (_addAdminRequestId) return;
_addAdminRequestId = MTP::send(MTPchannels_EditAdmin(_channel->inputChannel, _addAdmin->inputUser, MTP_channelRoleEditor()), rpcDone(&Inner::addAdminDone), rpcFail(&Inner::addAdminFail));
})), KeepOtherLayers);
} else if (sharingBotGame()) {
_addToPeer = peer;
auto confirmText = [peer] {
if (peer->isUser()) {
return lng_bot_sure_share_game(lt_user, App::peerName(peer));
}
return lng_bot_sure_share_game_group(lt_group, peer->name);
};
Ui::show(Box<ConfirmBox>(confirmText(), base::lambda_guarded(this, [this] {
addBot();
})), KeepOtherLayers);
} else if (bot() && (peer->isChat() || peer->isMegagroup())) {
_addToPeer = peer;
Ui::show(Box<ConfirmBox>(lng_bot_sure_invite(lt_group, peer->name), base::lambda_guarded(this, [this] {
addBot();
})), KeepOtherLayers);
} else {
Ui::hideSettingsAndLayer(true);
App::main()->choosePeer(peer->id, ShowAtUnreadMsgId);
}
if (i == l) {
d_byUsername.push_back(data);
_byUsername.push_back(peer);
for (i = 0, l = _byUsernameDatas.size(); i < l;) {
if (_byUsernameDatas[i] == data) {
_byUsernameDatas.removeAt(i);
--l;
} else {
++i;
}
}
}
changeCheckState(data, peer);
} else if (_filteredSelected >= 0 && _filteredSelected < _filtered.size()) {
auto data = contactData(_filtered[_filteredSelected]);
auto peer = _filtered[_filteredSelected]->history()->peer;
if (data->disabledChecked) return;
changeCheckState(data, peer);
}
}
}
PeerData *ContactsBox::Inner::selectedPeer() const {
if (_filter.isEmpty()) {
if (_searchedSelected >= 0 && _searchedSelected < _byUsername.size()) {
return _byUsername[_searchedSelected];
} else if (_selected) {
return _selected->history()->peer;
}
} else {
if (_searchedSelected >= 0 && _searchedSelected < _byUsernameFiltered.size()) {
return _byUsernameFiltered[_searchedSelected];
} else if (_filteredSelected >= 0 && _filteredSelected < _filtered.size()) {
return _filtered[_filteredSelected]->history()->peer;
}
}
return nullptr;
}
void ContactsBox::Inner::chooseParticipant() {
if (_saving) {
return;
}
if (usingMultiSelect()) {
changeMultiSelectCheckState();
} else {
if (_channel && _membersFilter == MembersFilter::Admins) {
Unexpected("Not supported any more");
} else if (sharingBotGame()) {
shareBotGameToSelected();
} else if (bot()) {
addBotToSelectedGroup();
} else if (auto peer = selectedPeer()) {
Ui::hideSettingsAndLayer(true);
App::main()->choosePeer(peer->id, ShowAtUnreadMsgId);
}
}
update();
}
void ContactsBox::Inner::shareBotGameToSelected() {
_addToPeer = selectedPeer();
if (!_addToPeer) {
return;
}
auto confirmText = [this] {
if (_addToPeer->isUser()) {
return lng_bot_sure_share_game(lt_user, App::peerName(_addToPeer));
}
return lng_bot_sure_share_game_group(lt_group, _addToPeer->name);
};
Ui::show(Box<ConfirmBox>(confirmText(), base::lambda_guarded(this, [this] {
addBot();
})), KeepOtherLayers);
}
void ContactsBox::Inner::addBotToSelectedGroup() {
_addToPeer = selectedPeer();
if (!_addToPeer) {
return;
}
if (auto megagroup = _addToPeer->asMegagroup()) {
if (!megagroup->canAddMembers()) {
Ui::show(Box<InformBox>(lang(lng_error_cant_add_member)), KeepOtherLayers);
return;
}
}
if (_addToPeer->isChat() || _addToPeer->isMegagroup()) {
Ui::show(Box<ConfirmBox>(lng_bot_sure_invite(lt_group, _addToPeer->name), base::lambda_guarded(this, [this] {
addBot();
})), KeepOtherLayers);
}
}
void ContactsBox::Inner::changeCheckState(Dialogs::Row *row) {
changeCheckState(contactData(row), row->history()->peer);
}
@@ -1453,7 +1471,7 @@ void ContactsBox::Inner::changeCheckState(ContactData *data, PeerData *peer) {
} else if (selectedCount() < ((_channel && _channel->isMegagroup()) ? Global::MegagroupSizeMax() : Global::ChatSizeMax())) {
changePeerCheckState(data, peer, true);
} else if (_channel && !_channel->isMegagroup()) {
Ui::show(Box<MaxInviteBox>(_channel->inviteLink()), KeepOtherLayers);
Ui::show(Box<MaxInviteBox>(_channel), KeepOtherLayers);
} else if (!_channel && selectedCount() >= Global::ChatSizeMax() && selectedCount() < Global::MegagroupSizeMax()) {
Ui::show(Box<InformBox>(lng_profile_add_more_after_upgrade(lt_count, Global::MegagroupSizeMax())), KeepOtherLayers);
}
@@ -1539,22 +1557,11 @@ void ContactsBox::Inner::updateSelection() {
void ContactsBox::Inner::updateFilter(QString filter) {
_lastQuery = filter.toLower().trimmed();
filter = textSearchKey(filter);
auto words = TextUtilities::PrepareSearchWords(_lastQuery);
filter = words.isEmpty() ? QString() : words.join(' ');
_time = unixtime();
QStringList f;
if (!filter.isEmpty()) {
QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts);
int l = filterList.size();
f.reserve(l);
for (int i = 0; i < l; ++i) {
QString filterName = filterList[i].trimmed();
if (filterName.isEmpty()) continue;
f.push_back(filterName);
}
filter = f.join(' ');
}
if (_filter != filter) {
_filter = filter;
@@ -1573,10 +1580,10 @@ void ContactsBox::Inner::updateFilter(QString filter) {
} else {
if (!_addContactLnk->isHidden()) _addContactLnk->hide();
if (!_allAdmins->isHidden()) _allAdmins->hide();
QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi;
QStringList::const_iterator fb = words.cbegin(), fe = words.cend(), fi;
_filtered.clear();
if (!f.isEmpty()) {
if (!words.isEmpty()) {
const Dialogs::List *toFilter = nullptr;
if (!_contacts->isEmpty()) {
for (fi = fb; fi != fe; ++fi) {
@@ -1967,8 +1974,8 @@ void ContactsBox::Inner::selectSkipPage(int32 h, int32 dir) {
selectSkip(points * dir);
}
QVector<UserData*> ContactsBox::Inner::selected() {
QVector<UserData*> result;
std::vector<gsl::not_null<UserData*>> ContactsBox::Inner::selected() {
std::vector<gsl::not_null<UserData*>> result;
if (!usingMultiSelect()) {
return result;
}
@@ -1980,13 +1987,17 @@ QVector<UserData*> ContactsBox::Inner::selected() {
}
result.reserve(_contactsData.size());
for (auto i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) {
if (i.value()->checkbox->checked() && i.key()->isUser()) {
result.push_back(i.key()->asUser());
if (i.value()->checkbox->checked()) {
if (auto user = i.key()->asUser()) {
result.push_back(user);
}
}
}
for (int i = 0, l = _byUsername.size(); i < l; ++i) {
if (d_byUsername[i]->checkbox->checked() && _byUsername[i]->isUser()) {
result.push_back(_byUsername[i]->asUser());
if (d_byUsername[i]->checkbox->checked()) {
if (auto user = _byUsername[i]->asUser()) {
result.push_back(user);
}
}
}
return result;

View File

@@ -23,7 +23,29 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "boxes/abstract_box.h"
#include "core/single_timer.h"
#include "ui/effects/round_checkbox.h"
#include "boxes/members_box.h"
enum class MembersFilter {
Recent,
Admins,
};
using MembersAlreadyIn = OrderedSet<UserData*>;
// Not used for now.
//
//class MembersAddButton : public Ui::RippleButton {
//public:
// MembersAddButton(QWidget *parent, const style::TwoIconButton &st);
//
//protected:
// void paintEvent(QPaintEvent *e) override;
//
// QImage prepareRippleMask() const override;
// QPoint prepareRippleStartPosition() const override;
//
//private:
// const style::TwoIconButton &_st;
//
//};
namespace Dialogs {
class Row;
@@ -80,6 +102,7 @@ protected:
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private:
object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>> createMultiSelect();
@@ -103,8 +126,8 @@ private:
void saveAdminsDone(const MTPUpdates &result);
void saveSelectedAdmins();
void getAdminsDone(const MTPmessages_ChatFull &result);
void setAdminDone(UserData *user, const MTPBool &result);
void removeAdminDone(UserData *user, const MTPBool &result);
void setAdminDone(gsl::not_null<UserData*> user, const MTPBool &result);
void removeAdminDone(gsl::not_null<UserData*> user, const MTPBool &result);
bool saveAdminsFail(const RPCError &error);
bool editAdminFail(const RPCError &error);
@@ -161,7 +184,7 @@ public:
void selectSkip(int32 dir);
void selectSkipPage(int32 h, int32 dir);
QVector<UserData*> selected();
std::vector<gsl::not_null<UserData*>> selected();
QVector<MTPInputUser> selectedInputs();
bool allAdmins() const;
void setAllAdminsChangedCallback(base::lambda<void()> allAdminsChangedCallback) {
@@ -249,8 +272,6 @@ private:
void updateSelectedRow();
int getRowTopWithPeer(PeerData *peer) const;
void updateRowWithPeer(PeerData *peer);
void addAdminDone(const MTPUpdates &result, mtpRequestId req);
bool addAdminFail(const RPCError &error, mtpRequestId req);
void paintDialog(Painter &p, TimeMs ms, PeerData *peer, ContactData *data, bool sel);
void paintDisabledCheckUserpic(Painter &p, PeerData *peer, int x, int y, int outerWidth) const;
@@ -266,9 +287,13 @@ private:
template <typename FilterCallback>
void addDialogsToList(FilterCallback callback);
PeerData *selectedPeer() const;
bool usingMultiSelect() const {
return (_chat != nullptr) || (_creating != CreatingGroupNone && (!_channel || _membersFilter != MembersFilter::Admins));
}
void changeMultiSelectCheckState();
void shareBotGameToSelected();
void addBotToSelectedGroup();
base::lambda<void(PeerData *peer, bool selected)> _peerSelectedChangedCallback;
@@ -291,9 +316,6 @@ private:
base::lambda<void()> _allAdminsChangedCallback;
PeerData *_addToPeer = nullptr;
UserData *_addAdmin = nullptr;
mtpRequestId _addAdminRequestId = 0;
QPointer<ConfirmBox> _addAdminBox;
int32 _time;

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/download_path_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "core/file_utilities.h"
#include "ui/widgets/checkbox.h"
@@ -39,10 +39,10 @@ DownloadPathBox::DownloadPathBox(QWidget *parent)
}
void DownloadPathBox::prepare() {
addButton(lang(lng_connection_save), [this] { save(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_connection_save), [this] { save(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
setTitle(lang(lng_download_path_header));
setTitle(langFactory(lng_download_path_header));
_group->setChangedCallback([this](Directory value) { radioChanged(value); });
@@ -57,11 +57,11 @@ void DownloadPathBox::updateControlsVisibility() {
auto custom = (_group->value() == Directory::Custom);
_pathLink->setVisible(custom);
auto newHeight = st::boxOptionListPadding.top() + _default->heightNoMargins() + st::boxOptionListSkip + _temp->heightNoMargins() + st::boxOptionListSkip + _dir->heightNoMargins();
auto newHeight = st::boxOptionListPadding.top() + _default->getMargins().top() + _default->heightNoMargins() + st::boxOptionListSkip + _temp->heightNoMargins() + st::boxOptionListSkip + _dir->heightNoMargins();
if (custom) {
newHeight += st::downloadPathSkip + _pathLink->height();
}
newHeight += st::boxOptionListPadding.bottom();
newHeight += st::boxOptionListPadding.bottom() + _dir->getMargins().bottom();
setDimensions(st::boxWideWidth, newHeight);
}
@@ -69,10 +69,10 @@ void DownloadPathBox::updateControlsVisibility() {
void DownloadPathBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_default->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxOptionListPadding.top());
_default->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxOptionListPadding.top() + _default->getMargins().top());
_temp->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _default->bottomNoMargins() + st::boxOptionListSkip);
_dir->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _temp->bottomNoMargins() + st::boxOptionListSkip);
auto inputx = st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultBoxCheckbox.textPosition.x();
auto inputx = st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultCheck.diameter + st::defaultBoxCheckbox.textPosition.x();
auto inputy = _dir->bottomNoMargins() + st::downloadPathSkip;
_pathLink->moveToLeft(inputx, inputy);
@@ -132,6 +132,6 @@ void DownloadPathBox::save() {
}
void DownloadPathBox::setPathText(const QString &text) {
auto availw = st::boxWideWidth - st::boxPadding.left() - st::defaultBoxCheckbox.textPosition.x() - st::boxPadding.right();
auto availw = st::boxWideWidth - st::boxPadding.left() - st::defaultCheck.diameter - st::defaultBoxCheckbox.textPosition.x() - st::boxPadding.right();
_pathLink->setText(st::boxTextFont->elided(text, availw));
}

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/edit_color_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "styles/style_boxes.h"
#include "ui/widgets/shadow.h"
#include "styles/style_mediaview.h"
@@ -643,7 +643,7 @@ EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : Box
}
void EditColorBox::prepare() {
setTitle(_title);
setTitle([this] { return _title; });
connect(_hueField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
connect(_saturationField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
@@ -661,8 +661,8 @@ void EditColorBox::prepare() {
connect(_blueField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
connect(_result, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
addButton(lang(lng_settings_save), [this] { saveColor(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_settings_save), [this] { saveColor(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
auto height = st::colorEditSkip + st::colorPickerSize + st::colorEditSkip + st::colorSliderWidth + st::colorEditSkip;
setDimensions(st::colorEditWidth, height);

View File

@@ -0,0 +1,510 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/edit_participant_box.h"
#include "lang/lang_keys.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "styles/style_boxes.h"
#include "styles/style_profile.h"
#include "ui/special_buttons.h"
#include "boxes/calendar_box.h"
namespace {
constexpr auto kMaxRestrictDelayDays = 366;
constexpr auto kSecondsInDay = 24 * 60 * 60;
constexpr auto kSecondsInWeek = 7 * kSecondsInDay;
template <typename CheckboxesMap, typename DependenciesMap>
void ApplyDependencies(CheckboxesMap &checkboxes, DependenciesMap &dependencies, QPointer<Ui::Checkbox> changed) {
auto checkAndApply = [&checkboxes](auto &&current, auto dependency, bool isChecked) {
for (auto &&checkbox : checkboxes) {
if ((checkbox.first & dependency) && (checkbox.second->checked() == isChecked)) {
current->setChecked(isChecked);
return true;
}
}
return false;
};
auto applySomeDependency = [&checkboxes, &dependencies, &changed, checkAndApply] {
auto result = false;
for (auto &&entry : checkboxes) {
if (entry.second == changed) {
continue;
}
auto isChecked = entry.second->checked();
for (auto &&dependency : dependencies) {
if (entry.first & (isChecked ? dependency.first : dependency.second)) {
if (checkAndApply(entry.second, (isChecked ? dependency.second : dependency.first), !isChecked)) {
result = true;
break;
}
}
}
}
return result;
};
while (true) {
if (!applySomeDependency()) {
break;
}
};
}
} // namespace
class EditParticipantBox::Divider : public TWidget {
public:
Divider(QWidget *parent) : TWidget(parent) {
}
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
};
int EditParticipantBox::Divider::resizeGetHeight(int newWidth) {
return st::rightsDividerHeight;
}
void EditParticipantBox::Divider::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), st::contactsAboutBg);
auto dividerFillTop = myrtlrect(0, 0, width(), st::profileDividerTop.height());
st::profileDividerTop.fill(p, dividerFillTop);
auto dividerFillBottom = myrtlrect(0, height() - st::profileDividerBottom.height(), width(), st::profileDividerBottom.height());
st::profileDividerBottom.fill(p, dividerFillBottom);
}
class EditParticipantBox::Inner : public TWidget {
public:
Inner(QWidget *parent, gsl::not_null<ChannelData*> channel, gsl::not_null<UserData*> user, bool hasAdminRights);
template <typename Widget>
QPointer<Widget> addControl(object_ptr<Widget> widget, QMargins margin) {
doAddControl(std::move(widget), margin);
return static_cast<Widget*>(_rows.back().widget.data());
}
void removeControl(QPointer<TWidget> widget);
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
void doAddControl(object_ptr<TWidget> widget, QMargins margin);
gsl::not_null<ChannelData*> _channel;
gsl::not_null<UserData*> _user;
object_ptr<Ui::PeerAvatarButton> _userPhoto;
Text _userName;
bool _hasAdminRights = false;
struct Control {
object_ptr<TWidget> widget;
QMargins margin;
};
std::vector<Control> _rows;
};
EditParticipantBox::Inner::Inner(QWidget *parent, gsl::not_null<ChannelData*> channel, gsl::not_null<UserData*> user, bool hasAdminRights) : TWidget(parent)
, _channel(channel)
, _user(user)
, _userPhoto(this, _user, st::rightsPhotoButton)
, _hasAdminRights(hasAdminRights) {
_userName.setText(st::rightsNameStyle, App::peerName(_user), _textNameOptions);
_userPhoto->setClickedCallback([this] { Ui::showPeerProfile(_user); });
}
void EditParticipantBox::Inner::removeControl(QPointer<TWidget> widget) {
auto row = std::find_if(_rows.begin(), _rows.end(), [widget](auto &&row) {
return (row.widget == widget);
});
t_assert(row != _rows.end());
row->widget.destroy();
_rows.erase(row);
}
void EditParticipantBox::Inner::doAddControl(object_ptr<TWidget> widget, QMargins margin) {
widget->setParent(this);
_rows.push_back({ std::move(widget), margin });
_rows.back().widget->show();
}
int EditParticipantBox::Inner::resizeGetHeight(int newWidth) {
_userPhoto->moveToLeft(st::rightsPhotoMargin.left(), st::rightsPhotoMargin.top());
auto newHeight = st::rightsPhotoMargin.top() + st::rightsPhotoButton.size + st::rightsPhotoMargin.bottom();
for (auto &&row : _rows) {
auto rowWidth = newWidth - row.margin.left() - row.margin.right();
newHeight += row.margin.top();
row.widget->resizeToNaturalWidth(rowWidth);
row.widget->moveToLeft(row.margin.left(), newHeight);
newHeight += row.widget->heightNoMargins() + row.margin.bottom();
}
return newHeight;
}
void EditParticipantBox::Inner::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), st::boxBg);
p.setPen(st::contactsNameFg);
auto namex = st::rightsPhotoMargin.left() + st::rightsPhotoButton.size + st::rightsPhotoMargin.right();
auto namew = width() - namex - st::rightsPhotoMargin.right();
_userName.drawLeftElided(p, namex, st::rightsPhotoMargin.top() + st::rightsNameTop, namew, width());
auto statusText = [this] {
if (_user->botInfo) {
auto seesAllMessages = (_user->botInfo->readsAllHistory || _hasAdminRights);
return lang(seesAllMessages ? lng_status_bot_reads_all : lng_status_bot_not_reads_all);
}
return App::onlineText(_user->onlineTill, unixtime());
};
p.setFont(st::contactsStatusFont);
p.setPen(st::contactsStatusFg);
p.drawTextLeft(namex, st::rightsPhotoMargin.top() + st::rightsStatusTop, width(), statusText());
}
EditParticipantBox::EditParticipantBox(QWidget*, gsl::not_null<ChannelData*> channel, gsl::not_null<UserData*> user, bool hasAdminRights) : BoxContent()
, _channel(channel)
, _user(user)
, _hasAdminRights(hasAdminRights) {
}
void EditParticipantBox::prepare() {
_inner = setInnerWidget(object_ptr<Inner>(this, _channel, _user, hasAdminRights()));
}
template <typename Widget>
QPointer<Widget> EditParticipantBox::addControl(object_ptr<Widget> widget, QMargins margin) {
Expects(_inner != nullptr);
return _inner->addControl(std::move(widget), margin);
}
void EditParticipantBox::removeControl(QPointer<TWidget> widget) {
Expects(_inner != nullptr);
return _inner->removeControl(widget);
}
void EditParticipantBox::resizeToContent() {
_inner->resizeToWidth(st::boxWideWidth);
setDimensions(_inner->width(), qMin(_inner->height(), st::boxMaxListHeight));
}
EditAdminBox::EditAdminBox(QWidget*, gsl::not_null<ChannelData*> channel, gsl::not_null<UserData*> user, const MTPChannelAdminRights &rights) : EditParticipantBox(nullptr, channel, user, (rights.c_channelAdminRights().vflags.v != 0))
, _oldRights(rights) {
auto dependency = [this](Flag dependent, Flag dependency) {
_dependencies.push_back(std::make_pair(dependent, dependency));
};
dependency(Flag::f_invite_link, Flag::f_invite_users); // invite_link <-> invite_users
dependency(Flag::f_invite_users, Flag::f_invite_link);
}
MTPChannelAdminRights EditAdminBox::DefaultRights(gsl::not_null<ChannelData*> channel) {
auto defaultRights = channel->isMegagroup()
? (Flag::f_change_info | Flag::f_delete_messages | Flag::f_ban_users | Flag::f_invite_users | Flag::f_invite_link | Flag::f_pin_messages)
: (Flag::f_change_info | Flag::f_post_messages | Flag::f_edit_messages | Flag::f_delete_messages | Flag::f_invite_users | Flag::f_invite_link);
return MTP_channelAdminRights(MTP_flags(defaultRights));
}
void EditAdminBox::prepare() {
EditParticipantBox::prepare();
auto hadRights = _oldRights.c_channelAdminRights().vflags.v;
setTitle(langFactory(hadRights ? lng_rights_edit_admin : lng_channel_add_admin));
addControl(object_ptr<Divider>(this), QMargins());
addControl(object_ptr<Ui::FlatLabel>(this, lang(lng_rights_edit_admin_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin);
auto prepareRights = (hadRights ? _oldRights : DefaultRights(channel()));
auto addCheckbox = [this, &prepareRights](Flags flags, const QString &text) {
auto checked = (prepareRights.c_channelAdminRights().vflags.v & flags) != 0;
auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::rightsCheckbox, st::rightsToggle), st::rightsToggleMargin);
subscribe(control->checkedChanged, [this, control](bool checked) {
InvokeQueued(this, [this, control] { applyDependencies(control); });
});
if (!channel()->amCreator()) {
if (!(channel()->adminRights().vflags.v & flags)) {
control->setDisabled(true); // Grey out options that we don't have ourselves.
}
}
if (!canSave()) {
control->setDisabled(true);
}
_checkboxes.emplace(flags, control);
};
if (channel()->isMegagroup()) {
addCheckbox(Flag::f_change_info, lang(lng_rights_group_info));
addCheckbox(Flag::f_delete_messages, lang(lng_rights_group_delete));
addCheckbox(Flag::f_ban_users, lang(lng_rights_group_ban));
addCheckbox(Flag::f_invite_users | Flag::f_invite_link, lang(channel()->anyoneCanAddMembers() ? lng_rights_group_invite_link : lng_rights_group_invite));
addCheckbox(Flag::f_pin_messages, lang(lng_rights_group_pin));
addCheckbox(Flag::f_add_admins, lang(lng_rights_add_admins));
} else {
addCheckbox(Flag::f_change_info, lang(lng_rights_channel_info));
addCheckbox(Flag::f_post_messages, lang(lng_rights_channel_post));
addCheckbox(Flag::f_edit_messages, lang(lng_rights_channel_edit));
addCheckbox(Flag::f_delete_messages, lang(lng_rights_channel_delete));
addCheckbox(Flag::f_invite_users | Flag::f_invite_link, lang(lng_rights_group_invite));
addCheckbox(Flag::f_add_admins, lang(lng_rights_add_admins));
}
auto addAdmins = _checkboxes.find(Flag::f_add_admins);
if (addAdmins != _checkboxes.end()) {
_aboutAddAdmins = addControl(object_ptr<Ui::FlatLabel>(this, st::boxLabel), st::rightsAboutMargin);
t_assert(addAdmins != _checkboxes.end());
subscribe(addAdmins->second->checkedChanged, [this](bool checked) {
refreshAboutAddAdminsText();
});
refreshAboutAddAdminsText();
}
if (canSave()) {
addButton(langFactory(lng_settings_save), [this] {
if (!_saveCallback) {
return;
}
auto newFlags = MTPDchannelAdminRights::Flags(0);
for (auto &&checkbox : _checkboxes) {
if (checkbox.second->checked()) {
newFlags |= checkbox.first;
} else {
newFlags &= ~checkbox.first;
}
}
if (!channel()->amCreator()) {
// Leave only rights that we have so we could save them.
newFlags &= channel()->adminRights().vflags.v;
}
_saveCallback(_oldRights, MTP_channelAdminRights(MTP_flags(newFlags)));
});
addButton(langFactory(lng_cancel), [this] { closeBox(); });
} else {
addButton(langFactory(lng_box_ok), [this] { closeBox(); });
}
applyDependencies(nullptr);
for (auto &&checkbox : _checkboxes) {
checkbox.second->finishAnimations();
}
resizeToContent();
}
void EditAdminBox::applyDependencies(QPointer<Ui::Checkbox> changed) {
ApplyDependencies(_checkboxes, _dependencies, changed);
}
void EditAdminBox::refreshAboutAddAdminsText() {
auto addAdmins = _checkboxes.find(Flag::f_add_admins);
t_assert(addAdmins != _checkboxes.end());
auto text = [this, addAdmins] {
if (!canSave()) {
return lang(lng_rights_about_admin_cant_edit);
} else if (addAdmins->second->checked()) {
return lang(lng_rights_about_add_admins_yes);
}
return lang(lng_rights_about_add_admins_no);
};
_aboutAddAdmins->setText(text());
resizeToContent();
}
EditRestrictedBox::EditRestrictedBox(QWidget*, gsl::not_null<ChannelData*> channel, gsl::not_null<UserData*> user, bool hasAdminRights, const MTPChannelBannedRights &rights) : EditParticipantBox(nullptr, channel, user, hasAdminRights)
, _oldRights(rights) {
auto dependency = [this](Flag dependent, Flag dependency) {
_dependencies.push_back(std::make_pair(dependent, dependency));
};
dependency(Flag::f_send_gifs, Flag::f_send_stickers); // stickers <-> gifs
dependency(Flag::f_send_stickers, Flag::f_send_gifs);
dependency(Flag::f_send_games, Flag::f_send_stickers); // stickers <-> games
dependency(Flag::f_send_stickers, Flag::f_send_games);
dependency(Flag::f_send_inline, Flag::f_send_stickers); // stickers <-> inline
dependency(Flag::f_send_stickers, Flag::f_send_inline);
dependency(Flag::f_send_stickers, Flag::f_send_media); // stickers -> send_media
dependency(Flag::f_embed_links, Flag::f_send_media); // embed_links -> send_media
dependency(Flag::f_send_media, Flag::f_send_messages); // send_media- > send_messages
dependency(Flag::f_send_messages, Flag::f_view_messages); // send_messages -> view_messages
}
void EditRestrictedBox::prepare() {
EditParticipantBox::prepare();
setTitle(langFactory(lng_rights_user_restrictions));
addControl(object_ptr<Divider>(this), QMargins());
addControl(object_ptr<Ui::FlatLabel>(this, lang(lng_rights_user_restrictions_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin);
auto prepareRights = (_oldRights.c_channelBannedRights().vflags.v ? _oldRights : DefaultRights(channel()));
_until = prepareRights.c_channelBannedRights().vuntil_date.v;
auto addCheckbox = [this, &prepareRights](Flags flags, const QString &text) {
auto checked = (prepareRights.c_channelBannedRights().vflags.v & flags) == 0;
auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::rightsCheckbox, st::rightsToggle), st::rightsToggleMargin);
subscribe(control->checkedChanged, [this, control](bool checked) {
InvokeQueued(this, [this, control] { applyDependencies(control); });
});
if (!canSave()) {
control->setDisabled(true);
}
_checkboxes.emplace(flags, control);
};
addCheckbox(Flag::f_view_messages, lang(lng_rights_chat_read));
addCheckbox(Flag::f_send_messages, lang(lng_rights_chat_send_text));
addCheckbox(Flag::f_send_media, lang(lng_rights_chat_send_media));
addCheckbox(Flag::f_send_stickers | Flag::f_send_gifs | Flag::f_send_games | Flag::f_send_inline, lang(lng_rights_chat_send_stickers));
addCheckbox(Flag::f_embed_links, lang(lng_rights_chat_send_links));
addControl(object_ptr<Divider>(this), st::rightsUntilMargin);
addControl(object_ptr<Ui::FlatLabel>(this, lang(lng_rights_chat_banned_until_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin);
setRestrictUntil(_until);
//addControl(object_ptr<Ui::LinkButton>(this, lang(lng_rights_chat_banned_block), st::boxLinkButton));
if (canSave()) {
addButton(langFactory(lng_settings_save), [this] {
if (!_saveCallback) {
return;
}
auto newFlags = MTPDchannelBannedRights::Flags(0);
for (auto &&checkbox : _checkboxes) {
if (checkbox.second->checked()) {
newFlags &= ~checkbox.first;
} else {
newFlags |= checkbox.first;
}
}
_saveCallback(_oldRights, MTP_channelBannedRights(MTP_flags(newFlags), MTP_int(getRealUntilValue())));
});
addButton(langFactory(lng_cancel), [this] { closeBox(); });
} else {
addButton(langFactory(lng_box_ok), [this] { closeBox(); });
}
applyDependencies(nullptr);
for (auto &&checkbox : _checkboxes) {
checkbox.second->finishAnimations();
}
resizeToContent();
}
void EditRestrictedBox::applyDependencies(QPointer<Ui::Checkbox> changed) {
ApplyDependencies(_checkboxes, _dependencies, changed);
}
MTPChannelBannedRights EditRestrictedBox::DefaultRights(gsl::not_null<ChannelData*> channel) {
auto defaultRights = Flag::f_send_messages | Flag::f_send_media | Flag::f_embed_links | Flag::f_send_stickers | Flag::f_send_gifs | Flag::f_send_games | Flag::f_send_inline;
return MTP_channelBannedRights(MTP_flags(defaultRights), MTP_int(0));
}
void EditRestrictedBox::showRestrictUntil() {
auto tomorrow = QDate::currentDate().addDays(1);
auto highlighted = isUntilForever() ? tomorrow : date(getRealUntilValue()).date();
auto month = highlighted;
_restrictUntilBox = Ui::show(Box<CalendarBox>(month, highlighted, [this](const QDate &date) { setRestrictUntil(static_cast<int>(QDateTime(date).toTime_t())); }), KeepOtherLayers);
_restrictUntilBox->setMaxDate(QDate::currentDate().addDays(kMaxRestrictDelayDays));
_restrictUntilBox->setMinDate(tomorrow);
_restrictUntilBox->addLeftButton(langFactory(lng_rights_chat_banned_forever), [this] { setRestrictUntil(0); });
}
void EditRestrictedBox::setRestrictUntil(TimeId until) {
_until = until;
if (_restrictUntilBox) {
_restrictUntilBox->closeBox();
}
clearVariants();
createUntilGroup();
createUntilVariants();
resizeToContent();
}
void EditRestrictedBox::clearVariants() {
for (auto &&widget : base::take(_untilVariants)) {
removeControl(widget.data());
}
}
void EditRestrictedBox::createUntilGroup() {
_untilGroup = std::make_shared<Ui::RadiobuttonGroup>(isUntilForever() ? 0 : _until);
_untilGroup->setChangedCallback([this](int value) {
if (value == kUntilCustom) {
_untilGroup->setValue(_until);
showRestrictUntil();
} else if (_until != value) {
_until = value;
}
});
}
void EditRestrictedBox::createUntilVariants() {
auto addVariant = [this](int value, const QString &text) {
if (!canSave() && _untilGroup->value() != value) {
return;
}
_untilVariants.push_back(addControl(object_ptr<Ui::Radiobutton>(this, _untilGroup, value, text, st::defaultBoxCheckbox), st::rightsToggleMargin));
if (!canSave()) {
_untilVariants.back()->setDisabled(true);
}
};
auto addCustomVariant = [this, addVariant](TimeId until, TimeId from, TimeId to) {
if (!ChannelData::IsRestrictedForever(until) && until > from && until <= to) {
addVariant(until, lng_rights_chat_banned_custom_date(lt_date, langDayOfMonthFull(date(until).date())));
}
};
auto addCurrentVariant = [this, addCustomVariant](TimeId from, TimeId to) {
auto oldUntil = _oldRights.c_channelBannedRights().vuntil_date.v;
if (oldUntil < _until) {
addCustomVariant(oldUntil, from, to);
}
addCustomVariant(_until, from, to);
if (oldUntil > _until) {
addCustomVariant(oldUntil, from, to);
}
};
addVariant(0, lang(lng_rights_chat_banned_forever));
auto now = unixtime();
auto nextDay = now + kSecondsInDay;
auto nextWeek = now + kSecondsInWeek;
addCurrentVariant(0, nextDay);
addVariant(kUntilOneDay, lng_rights_chat_banned_day(lt_count, 1));
addCurrentVariant(nextDay, nextWeek);
addVariant(kUntilOneWeek, lng_rights_chat_banned_week(lt_count, 1));
addCurrentVariant(nextWeek, INT_MAX);
addVariant(kUntilCustom, lang(lng_rights_chat_banned_custom));
}
TimeId EditRestrictedBox::getRealUntilValue() const {
Expects(_until != kUntilCustom);
if (_until == kUntilOneDay) {
return unixtime() + kSecondsInDay;
} else if (_until == kUntilOneWeek) {
return unixtime() + kSecondsInWeek;
}
t_assert(_until >= 0);
return _until;
}

View File

@@ -0,0 +1,153 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "boxes/abstract_box.h"
namespace Ui {
class FlatLabel;
class LinkButton;
class Checkbox;
class Radiobutton;
class RadiobuttonGroup;
} // namespace Ui
class CalendarBox;
class EditParticipantBox : public BoxContent {
public:
EditParticipantBox(QWidget*, gsl::not_null<ChannelData*> channel, gsl::not_null<UserData*> user, bool hasAdminRights);
protected:
void prepare() override;
void resizeToContent();
gsl::not_null<UserData*> user() const {
return _user;
}
gsl::not_null<ChannelData*> channel() const {
return _channel;
}
template <typename Widget>
QPointer<Widget> addControl(object_ptr<Widget> widget, QMargins margin);
void removeControl(QPointer<TWidget> widget);
bool hasAdminRights() const {
return _hasAdminRights;
}
class Divider;
private:
gsl::not_null<ChannelData*> _channel;
gsl::not_null<UserData*> _user;
bool _hasAdminRights = false;
class Inner;
QPointer<Inner> _inner;
};
class EditAdminBox : public EditParticipantBox {
public:
EditAdminBox(QWidget*, gsl::not_null<ChannelData*> channel, gsl::not_null<UserData*> user, const MTPChannelAdminRights &rights);
void setSaveCallback(base::lambda<void(MTPChannelAdminRights, MTPChannelAdminRights)> callback) {
_saveCallback = std::move(callback);
}
protected:
void prepare() override;
private:
using Flag = MTPDchannelAdminRights::Flag;
using Flags = MTPDchannelAdminRights::Flags;
static MTPChannelAdminRights DefaultRights(gsl::not_null<ChannelData*> channel);
bool canSave() const {
return !!_saveCallback;
}
void applyDependencies(QPointer<Ui::Checkbox> changed);
void refreshAboutAddAdminsText();
const MTPChannelAdminRights _oldRights;
std::vector<std::pair<Flag, Flag>> _dependencies;
base::lambda<void(MTPChannelAdminRights, MTPChannelAdminRights)> _saveCallback;
std::map<Flags, QPointer<Ui::Checkbox>> _checkboxes;
QPointer<Ui::FlatLabel> _aboutAddAdmins;
};
// Restricted box works with flags in the opposite way.
// If some flag is set in the rights then the checkbox is unchecked.
class EditRestrictedBox : public EditParticipantBox {
public:
EditRestrictedBox(QWidget*, gsl::not_null<ChannelData*> channel, gsl::not_null<UserData*> user, bool hasAdminRights, const MTPChannelBannedRights &rights);
void setSaveCallback(base::lambda<void(MTPChannelBannedRights, MTPChannelBannedRights)> callback) {
_saveCallback = std::move(callback);
}
protected:
void prepare() override;
private:
using Flag = MTPDchannelBannedRights::Flag;
using Flags = MTPDchannelBannedRights::Flags;
static MTPChannelBannedRights DefaultRights(gsl::not_null<ChannelData*> channel);
bool canSave() const {
return !!_saveCallback;
}
void applyDependencies(QPointer<Ui::Checkbox> changed);
void showRestrictUntil();
void setRestrictUntil(TimeId until);
bool isUntilForever() {
return ChannelData::IsRestrictedForever(_until);
}
void clearVariants();
void createUntilGroup();
void createUntilVariants();
TimeId getRealUntilValue() const;
const MTPChannelBannedRights _oldRights;
TimeId _until = 0;
std::vector<std::pair<Flag, Flag>> _dependencies;
base::lambda<void(MTPChannelBannedRights, MTPChannelBannedRights)> _saveCallback;
std::map<Flags, QPointer<Ui::Checkbox>> _checkboxes;
std::shared_ptr<Ui::RadiobuttonGroup> _untilGroup;
QVector<QPointer<Ui::Radiobutton>> _untilVariants;
QPointer<CalendarBox> _restrictUntilBox;
static constexpr auto kUntilOneDay = -1;
static constexpr auto kUntilOneWeek = -2;
static constexpr auto kUntilCustom = -3;
};

View File

@@ -27,57 +27,56 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/effects/widget_slide_wrap.h"
#include "boxes/peer_list_box.h"
#include "apiwrap.h"
#include "lang.h"
#include "lang/lang_keys.h"
namespace {
class PrivacyExceptionsBoxController : public ChatsListBoxController {
public:
PrivacyExceptionsBoxController(const QString &title, const QVector<UserData*> &selected, base::lambda_once<void(QVector<UserData*> &&result)> saveCallback);
void rowClicked(PeerListBox::Row *row) override;
PrivacyExceptionsBoxController(base::lambda<QString()> titleFactory, const std::vector<gsl::not_null<UserData*>> &selected);
void rowClicked(gsl::not_null<PeerListRow*> row) override;
std::vector<gsl::not_null<UserData*>> getResult() const;
protected:
void prepareViewHook() override;
std::unique_ptr<Row> createRow(History *history) override;
std::unique_ptr<Row> createRow(gsl::not_null<History*> history) override;
private:
QString _title;
QVector<UserData*> _selected;
base::lambda_once<void(QVector<UserData*> &&result)> _saveCallback;
base::lambda<QString()> _titleFactory;
std::vector<gsl::not_null<UserData*>> _selected;
};
PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(const QString &title, const QVector<UserData*> &selected, base::lambda_once<void(QVector<UserData*> &&result)> saveCallback)
: _title(title)
, _selected(selected)
, _saveCallback(std::move(saveCallback)) {
PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(base::lambda<QString()> titleFactory, const std::vector<gsl::not_null<UserData*>> &selected)
: _titleFactory(std::move(titleFactory))
, _selected(selected) {
}
void PrivacyExceptionsBoxController::prepareViewHook() {
view()->setTitle(_title);
view()->addButton(lang(lng_settings_save), [this] {
auto peers = view()->collectSelectedRows();
auto users = QVector<UserData*>();
if (!peers.empty()) {
users.reserve(peers.size());
for_const (auto peer, peers) {
auto user = peer->asUser();
t_assert(user != nullptr);
users.push_back(user);
}
delegate()->peerListSetTitle(_titleFactory);
delegate()->peerListAddSelectedRows(_selected);
}
std::vector<gsl::not_null<UserData*>> PrivacyExceptionsBoxController::getResult() const {
auto peers = delegate()->peerListCollectSelectedRows();
auto users = std::vector<gsl::not_null<UserData*>>();
if (!peers.empty()) {
users.reserve(peers.size());
for_const (auto peer, peers) {
auto user = peer->asUser();
t_assert(user != nullptr);
users.push_back(user);
}
_saveCallback(std::move(users));
view()->closeBox();
});
view()->addButton(lang(lng_cancel), [this] { view()->closeBox(); });
view()->addSelectedRows(_selected);
}
return users;
}
void PrivacyExceptionsBoxController::rowClicked(PeerListBox::Row *row) {
view()->setRowChecked(row, !row->checked());
void PrivacyExceptionsBoxController::rowClicked(gsl::not_null<PeerListRow*> row) {
delegate()->peerListSetRowChecked(row, !row->checked());
}
std::unique_ptr<PrivacyExceptionsBoxController::Row> PrivacyExceptionsBoxController::createRow(History *history) {
std::unique_ptr<PrivacyExceptionsBoxController::Row> PrivacyExceptionsBoxController::createRow(gsl::not_null<History*> history) {
if (auto user = history->peer->asUser()) {
if (!user->isSelf()) {
return std::make_unique<Row>(history);
@@ -96,8 +95,8 @@ EditPrivacyBox::EditPrivacyBox(QWidget*, std::unique_ptr<Controller> controller)
void EditPrivacyBox::prepare() {
_controller->setView(this);
setTitle(_controller->title());
addButton(lang(lng_cancel), [this] { closeBox(); });
setTitle([this] { return _controller->title(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
loadData();
@@ -146,7 +145,7 @@ int EditPrivacyBox::countDefaultHeight(int newWidth) {
if (!_controller->hasOption(option)) {
return 0;
}
return st::editPrivacyOptionMargin.top() + st::defaultBoxCheckbox.height + st::editPrivacyOptionMargin.bottom();
return st::editPrivacyOptionMargin.top() + st::defaultCheck.diameter + st::editPrivacyOptionMargin.bottom();
};
auto labelHeight = [this, newWidth](const QString &text, const style::FlatLabel &st, style::margins padding) {
if (text.isEmpty()) {
@@ -174,29 +173,37 @@ int EditPrivacyBox::countDefaultHeight(int newWidth) {
}
void EditPrivacyBox::editExceptionUsers(Exception exception) {
auto controller = std::make_unique<PrivacyExceptionsBoxController>(_controller->exceptionBoxTitle(exception), exceptionUsers(exception), base::lambda_guarded(this, [this, exception](QVector<UserData*> &&users) {
exceptionUsers(exception) = std::move(users);
exceptionLink(exception)->entity()->setText(exceptionLinkText(exception));
auto removeFrom = ([exception] {
switch (exception) {
case Exception::Always: return Exception::Never;
case Exception::Never: return Exception::Always;
auto controller = std::make_unique<PrivacyExceptionsBoxController>(base::lambda_guarded(this, [this, exception] {
return _controller->exceptionBoxTitle(exception);
}), exceptionUsers(exception));
auto initBox = [this, exception, controller = controller.get()](PeerListBox *box) {
box->addButton(langFactory(lng_settings_save), base::lambda_guarded(this, [this, box, exception, controller] {
exceptionUsers(exception) = controller->getResult();
exceptionLink(exception)->entity()->setText(exceptionLinkText(exception));
auto removeFrom = ([exception] {
switch (exception) {
case Exception::Always: return Exception::Never;
case Exception::Never: return Exception::Always;
}
Unexpected("Invalid exception value.");
})();
auto &removeFromUsers = exceptionUsers(removeFrom);
auto removedSome = false;
for (auto user : exceptionUsers(exception)) {
auto removedStart = std::remove(removeFromUsers.begin(), removeFromUsers.end(), user);
if (removedStart != removeFromUsers.end()) {
removeFromUsers.erase(removedStart, removeFromUsers.end());
removedSome = true;
}
}
Unexpected("Invalid exception value.");
})();
auto &removeFromUsers = exceptionUsers(removeFrom);
auto removedSome = false;
for (auto user : exceptionUsers(exception)) {
if (removeFromUsers.contains(user)) {
removeFromUsers.erase(std::remove(removeFromUsers.begin(), removeFromUsers.end(), user), removeFromUsers.end());
removedSome = true;
if (removedSome) {
exceptionLink(removeFrom)->entity()->setText(exceptionLinkText(removeFrom));
}
}
if (removedSome) {
exceptionLink(removeFrom)->entity()->setText(exceptionLinkText(removeFrom));
}
}));
Ui::show(Box<PeerListBox>(std::move(controller)), KeepOtherLayers);
box->closeBox();
}));
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
};
Ui::show(Box<PeerListBox>(std::move(controller), std::move(initBox)), KeepOtherLayers);
}
QString EditPrivacyBox::exceptionLinkText(Exception exception) {
@@ -235,7 +242,7 @@ style::margins EditPrivacyBox::exceptionLinkMargins() const {
return st::editPrivacyLinkMargin;
}
QVector<UserData*> &EditPrivacyBox::exceptionUsers(Exception exception) {
std::vector<gsl::not_null<UserData*>> &EditPrivacyBox::exceptionUsers(Exception exception) {
switch (exception) {
case Exception::Always: return _alwaysUsers;
case Exception::Never: return _neverUsers;
@@ -292,14 +299,14 @@ void EditPrivacyBox::createWidgets() {
createLabel(_exceptionsDescription, _controller->exceptionsDescription(), st::editPrivacyLabel);
clearButtons();
addButton(lang(lng_settings_save), [this] {
addButton(langFactory(lng_settings_save), [this] {
auto someAreDisallowed = (_option != Option::Everyone) || !_neverUsers.empty();
_controller->confirmSave(someAreDisallowed, base::lambda_guarded(this, [this] {
App::api()->savePrivacy(_controller->key(), collectResult());
closeBox();
}));
});
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
_optionGroup->setChangedCallback([this](Option value) {
_option = value;
@@ -337,7 +344,7 @@ void EditPrivacyBox::loadData() {
_alwaysUsers.reserve(_alwaysUsers.size() + users.size());
for (auto &userId : users) {
auto user = App::user(UserId(userId.v));
if (!_neverUsers.contains(user) && !_alwaysUsers.contains(user)) {
if (!base::contains(_neverUsers, user) && !base::contains(_alwaysUsers, user)) {
_alwaysUsers.push_back(user);
}
}
@@ -349,7 +356,7 @@ void EditPrivacyBox::loadData() {
_neverUsers.reserve(_neverUsers.size() + users.size());
for (auto &userId : users) {
auto user = App::user(UserId(userId.v));
if (!_alwaysUsers.contains(user) && !_neverUsers.contains(user)) {
if (!base::contains(_alwaysUsers, user) && !base::contains(_neverUsers, user)) {
_neverUsers.push_back(user);
}
}

View File

@@ -102,7 +102,7 @@ private:
void editExceptionUsers(Exception exception);
QString exceptionLinkText(Exception exception);
QVector<UserData*> &exceptionUsers(Exception exception);
std::vector<gsl::not_null<UserData*>> &exceptionUsers(Exception exception);
object_ptr<Ui::WidgetSlideWrap<Ui::LinkButton>> &exceptionLink(Exception exception);
std::unique_ptr<Controller> _controller;
@@ -120,7 +120,7 @@ private:
object_ptr<Ui::WidgetSlideWrap<Ui::LinkButton>> _neverLink = { nullptr };
object_ptr<Ui::FlatLabel> _exceptionsDescription = { nullptr };
QVector<UserData*> _alwaysUsers;
QVector<UserData*> _neverUsers;
std::vector<gsl::not_null<UserData*>> _alwaysUsers;
std::vector<gsl::not_null<UserData*>> _neverUsers;
};

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/emoji_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "mainwindow.h"
@@ -79,10 +79,10 @@ EmojiBox::EmojiBox(QWidget*) : _esize(Ui::Emoji::Size(Ui::Emoji::Index() + 1)) {
}
void EmojiBox::prepare() {
setTitle(lang(lng_settings_emoji_list));
setTitle(langFactory(lng_settings_emoji_list));
fillBlocks();
addButton(lang(lng_close), [this] { closeBox(); });
addButton(langFactory(lng_close), [this] { closeBox(); });
_blockHeight = st::emojiReplaceInnerHeight;

View File

@@ -20,90 +20,146 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/language_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "storage/localstorage.h"
#include "boxes/confirm_box.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "langloaderplain.h"
#include "lang/lang_instance.h"
#include "lang/lang_cloud_manager.h"
#include "styles/style_boxes.h"
class LanguageBox::Inner : public TWidget, private base::Subscriber {
public:
Inner(QWidget *parent, gsl::not_null<Languages*> languages);
void setSelected(int index);
void refresh();
private:
void activateCurrent();
void languageChanged(int languageIndex);
gsl::not_null<Languages*> _languages;
std::shared_ptr<Ui::RadiobuttonGroup> _group;
std::vector<object_ptr<Ui::Radiobutton>> _buttons;
};
LanguageBox::Inner::Inner(QWidget *parent, gsl::not_null<Languages*> languages) : TWidget(parent)
, _languages(languages) {
_group = std::make_shared<Ui::RadiobuttonGroup>(0);
_group->setChangedCallback([this](int value) { languageChanged(value); });
subscribe(Lang::Current().updated(), [this] {
activateCurrent();
refresh();
});
}
void LanguageBox::Inner::setSelected(int index) {
_group->setValue(index);
}
void LanguageBox::Inner::refresh() {
for (auto &button : _buttons) {
button.destroy();
}
_buttons.clear();
auto y = st::boxOptionListPadding.top() + st::langsButton.margin.top();
_buttons.reserve(_languages->size());
auto index = 0;
for_const (auto &language, *_languages) {
_buttons.emplace_back(this, _group, index++, language.nativeName, st::langsButton);
auto button = _buttons.back().data();
button->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), y);
button->show();
y += button->heightNoMargins() + st::boxOptionListSkip;
}
auto newHeight = y - st::boxOptionListSkip + st::boxOptionListPadding.bottom() + st::langsButton.margin.bottom();
resize(st::langsWidth, newHeight);
}
void LanguageBox::Inner::languageChanged(int languageIndex) {
Expects(languageIndex >= 0 && languageIndex < _languages->size());
activateCurrent();
auto languageId = (*_languages)[languageIndex].id;
if (Lang::Current().id() != languageId) {
// "custom" is applied each time it is passed to switchToLanguage().
// So we check that the language really has changed.
Lang::CurrentCloudManager().switchToLanguage(languageId);
}
}
void LanguageBox::Inner::activateCurrent() {
auto currentId = Lang::Current().id();
for (auto i = 0, count = _languages->size(); i != count; ++i) {
auto languageId = (*_languages)[i].id;
auto isCurrent = (languageId == currentId) || (languageId == Lang::DefaultLanguageId() && currentId.isEmpty());
if (isCurrent) {
_group->setValue(i);
return;
}
}
}
void LanguageBox::prepare() {
addButton(lang(lng_box_ok), [this] { closeBox(); });
refreshLang();
subscribe(Lang::Current().updated(), [this] {
refreshLang();
});
setTitle(lang(lng_languages));
_inner = setInnerWidget(object_ptr<Inner>(this, &_languages), st::boxLayerScroll);
auto haveTestLang = (cLang() == languageTest);
_langGroup = std::make_shared<Ui::RadiobuttonGroup>(cLang());
auto y = st::boxOptionListPadding.top();
_langs.reserve(languageCount + (haveTestLang ? 1 : 0));
if (haveTestLang) {
_langs.emplace_back(this, _langGroup, languageTest, qsl("Custom Lang"), st::langsButton);
_langs.back()->move(st::boxPadding.left() + st::boxOptionListPadding.left(), y);
y += _langs.back()->heightNoMargins() + st::boxOptionListSkip;
}
for (auto i = 0; i != languageCount; ++i) {
LangLoaderResult result;
if (i) {
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), langLoaderRequest(lng_language_name));
result = loader.found();
} else {
result.insert(lng_language_name, langOriginal(lng_language_name));
}
_langs.emplace_back(this, _langGroup, i, result.value(lng_language_name, LanguageCodes[i].c_str() + qsl(" language")), st::langsButton);
_langs.back()->move(st::boxPadding.left() + st::boxOptionListPadding.left(), y);
y += _langs.back()->heightNoMargins() + st::boxOptionListSkip;
}
_langGroup->setChangedCallback([this](int value) { languageChanged(value); });
auto optionsCount = languageCount + (haveTestLang ? 1 : 0);
setDimensions(st::langsWidth, st::boxOptionListPadding.top() + optionsCount * st::langsButton.height + (optionsCount - 1) * st::boxOptionListSkip + st::boxOptionListPadding.bottom() + st::boxPadding.bottom());
refresh();
subscribe(Lang::CurrentCloudManager().languageListChanged(), [this] {
refresh();
});
}
void LanguageBox::mousePressEvent(QMouseEvent *e) {
if ((e->modifiers() & Qt::CTRL) && (e->modifiers() & Qt::ALT) && (e->modifiers() & Qt::SHIFT)) {
for (int32 i = 1; i < languageCount; ++i) {
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), langLoaderRequest(lngkeys_cnt));
if (!loader.errors().isEmpty()) {
Ui::show(Box<InformBox>(qsl("Lang \"") + LanguageCodes[i].c_str() + qsl("\" error :(\n\nError: ") + loader.errors()));
return;
} else if (!loader.warnings().isEmpty()) {
QString warn = loader.warnings();
if (warn.size() > 256) warn = warn.mid(0, 253) + qsl("...");
Ui::show(Box<InformBox>(qsl("Lang \"") + LanguageCodes[i].c_str() + qsl("\" warnings :(\n\nWarnings: ") + warn));
return;
void LanguageBox::refreshLang() {
clearButtons();
addButton(langFactory(lng_box_ok), [this] { closeBox(); });
setTitle(langFactory(lng_languages));
update();
}
void LanguageBox::refresh() {
refreshLanguages();
_inner->refresh();
setDimensions(st::langsWidth, qMin(_inner->height(), st::boxMaxListHeight));
}
void LanguageBox::refreshLanguages() {
_languages = Languages();
auto list = Lang::CurrentCloudManager().languageList();
_languages.reserve(list.size() + 1);
auto currentId = Lang::Current().id();
auto currentIndex = -1;
_languages.push_back({ qsl("en"), qsl("English"), qsl("English") });
for (auto &language : list) {
auto isCurrent = (language.id == currentId) || (language.id == Lang::DefaultLanguageId() && currentId.isEmpty());
if (language.id != qstr("en")) {
if (isCurrent) {
currentIndex = _languages.size();
}
_languages.push_back(language);
} else if (isCurrent) {
currentIndex = 0;
}
Ui::show(Box<InformBox>(qsl("Everything seems great in all %1 languages!").arg(languageCount - 1)));
}
}
void LanguageBox::languageChanged(int languageId) {
Expects(languageId == languageTest || (languageId >= 0 && languageId < base::array_size(LanguageCodes)));
if (languageId == cLang()) {
return;
}
LangLoaderResult result;
if (languageId > 0) {
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[languageId].c_str() + qsl(".strings"), langLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok));
result = loader.found();
} else if (languageId == languageTest) {
LangLoaderPlain loader(cLangFile(), langLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok));
result = loader.found();
}
auto text = result.value(lng_sure_save_language, langOriginal(lng_sure_save_language)),
save = result.value(lng_box_ok, langOriginal(lng_box_ok)),
cancel = result.value(lng_cancel, langOriginal(lng_cancel));
Ui::show(Box<ConfirmBox>(text, save, cancel, base::lambda_guarded(this, [this, languageId] {
cSetLang(languageId);
Local::writeSettings();
App::restart();
}), base::lambda_guarded(this, [this] {
_langGroup->setValue(cLang());
})), KeepOtherLayers);
if (currentId == qstr("custom")) {
_languages.insert(_languages.begin(), { currentId, qsl("Custom LangPack"), qsl("Custom LangPack") });
currentIndex = 0;
} else if (currentIndex < 0) {
currentIndex = _languages.size();
_languages.push_back({ currentId, lang(lng_language_name), lang(lng_language_name) });
}
_inner->setSelected(currentIndex);
}

View File

@@ -20,16 +20,16 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "lang/lang_cloud_manager.h"
#include "boxes/abstract_box.h"
#include "mtproto/sender.h"
namespace Ui {
class RadiobuttonGroup;
class Radiobutton;
} // namespace Ui
class LanguageBox : public BoxContent {
Q_OBJECT
class LanguageBox : public BoxContent, private MTP::Sender {
public:
LanguageBox(QWidget*) {
}
@@ -37,12 +37,16 @@ public:
protected:
void prepare() override;
void mousePressEvent(QMouseEvent *e) override;
private:
void languageChanged(int languageId);
using Languages = Lang::CloudManager::Languages;
std::shared_ptr<Ui::RadiobuttonGroup> _langGroup;
std::vector<object_ptr<Ui::Radiobutton>> _langs;
void refresh();
void refreshLanguages();
void refreshLang();
Languages _languages;
class Inner;
QPointer<Inner> _inner;
};

View File

@@ -23,7 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_boxes.h"
#include "ui/widgets/buttons.h"
#include "storage/localstorage.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "mainwindow.h"
#include "auth_session.h"
@@ -32,9 +32,9 @@ LocalStorageBox::LocalStorageBox(QWidget *parent)
}
void LocalStorageBox::prepare() {
setTitle(lang(lng_local_storage_title));
setTitle(langFactory(lng_local_storage_title));
addButton(lang(lng_box_ok), [this] { closeBox(); });
addButton(langFactory(lng_box_ok), [this] { closeBox(); });
_clear->setClickedCallback([this] { clearStorage(); });

View File

@@ -1,656 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/members_box.h"
#include "styles/style_boxes.h"
#include "styles/style_dialogs.h"
#include "lang.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "boxes/contacts_box.h"
#include "boxes/confirm_box.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/effects/ripple_animation.h"
#include "observer_peer.h"
#include "auth_session.h"
#include "storage/file_download.h"
// Not used for now.
//
//MembersAddButton::MembersAddButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple)
//, _st(st) {
// resize(_st.width, _st.height);
// setCursor(style::cur_pointer);
//}
//
//void MembersAddButton::paintEvent(QPaintEvent *e) {
// Painter p(this);
//
// auto ms = getms();
// auto over = isOver();
// auto down = isDown();
//
// ((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width());
// paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms);
// ((over || down) ? _st.iconAboveOver : _st.iconAbove).paint(p, _st.iconPosition, width());
//}
//
//QImage MembersAddButton::prepareRippleMask() const {
// return Ui::RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize));
//}
//
//QPoint MembersAddButton::prepareRippleStartPosition() const {
// return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition;
//}
namespace {
constexpr auto kReloadChannelAdminsTimeout = 1000; // 1 second wait before reload admins in channel after adding
} // namespace
MembersBox::MembersBox(QWidget*, ChannelData *channel, MembersFilter filter)
: _channel(channel)
, _filter(filter) {
}
void MembersBox::prepare() {
setTitle(lang(_filter == MembersFilter::Recent ? lng_channel_members : lng_channel_admins));
_inner = setInnerWidget(object_ptr<Inner>(this, _channel, _filter), st::boxLayerScroll);
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
addButton(lang(lng_close), [this] { closeBox(); });
if (_channel->amCreator() && (_channel->membersCount() < (_channel->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax()) || (!_channel->isMegagroup() && !_channel->isPublic()) || _filter == MembersFilter::Admins)) {
addLeftButton(lang((_filter == MembersFilter::Admins) ? lng_channel_add_admin : lng_channel_add_members), [this] { onAdd(); });
}
connect(_inner, SIGNAL(mustScrollTo(int, int)), this, SLOT(onScrollToY(int, int)));
_loadTimer.create(this);
connect(_loadTimer, SIGNAL(timeout()), _inner, SLOT(load()));
}
void MembersBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Down) {
_inner->selectSkip(1);
} else if (e->key() == Qt::Key_Up) {
_inner->selectSkip(-1);
} else if (e->key() == Qt::Key_PageDown) {
_inner->selectSkipPage(height(), 1);
} else if (e->key() == Qt::Key_PageUp) {
_inner->selectSkipPage(height(), -1);
} else {
BoxContent::keyPressEvent(e);
}
}
void MembersBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_inner->resize(width(), _inner->height());
}
void MembersBox::onAdd() {
if (_inner->filter() == MembersFilter::Recent && _inner->channel()->membersCount() >= (_inner->channel()->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax())) {
Ui::show(Box<MaxInviteBox>(_inner->channel()->inviteLink()), KeepOtherLayers);
return;
}
auto box = Box<ContactsBox>(_inner->channel(), _inner->filter(), _inner->already());
if (_inner->filter() == MembersFilter::Recent) {
Ui::show(std::move(box));
} else {
_addBox = Ui::show(std::move(box), KeepOtherLayers);
if (_addBox) {
connect(_addBox, SIGNAL(adminAdded()), this, SLOT(onAdminAdded()));
}
}
}
void MembersBox::onAdminAdded() {
if (!_addBox) return;
_addBox->closeBox();
_addBox = nullptr;
_loadTimer->start(kReloadChannelAdminsTimeout);
}
MembersBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter filter) : TWidget(parent)
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _channel(channel)
, _filter(filter)
, _kickText(lang(lng_profile_kick))
, _kickWidth(st::normalFont->width(_kickText))
, _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right())
, _about(_aboutWidth) {
subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); });
connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)));
connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*)));
refresh();
load();
}
void MembersBox::Inner::load() {
if (!_loadingRequestId) {
_loadingRequestId = MTP::send(MTPchannels_GetParticipants(_channel->inputChannel, (_filter == MembersFilter::Recent) ? MTP_channelParticipantsRecent() : MTP_channelParticipantsAdmins(), MTP_int(0), MTP_int(Global::ChatSizeMax())), rpcDone(&Inner::membersReceived), rpcFail(&Inner::membersFailed));
}
}
void MembersBox::Inner::paintEvent(QPaintEvent *e) {
QRect r(e->rect());
Painter p(this);
_time = unixtime();
p.fillRect(r, st::contactsBg);
auto ms = getms();
auto yFrom = r.y() - st::membersMarginTop;
auto yTo = r.y() + r.height() - st::membersMarginTop;
p.translate(0, st::membersMarginTop);
if (_rows.isEmpty()) {
p.setFont(st::noContactsFont);
p.setPen(st::noContactsColor);
p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center);
} else {
int32 from = floorclamp(yFrom, _rowHeight, 0, _rows.size());
int32 to = ceilclamp(yTo, _rowHeight, 0, _rows.size());
p.translate(0, from * _rowHeight);
for (; from < to; ++from) {
auto selected = (_pressed >= 0) ? (from == _pressed) : (from == _selected);
auto kickSelected = (_pressed >= 0) ? (from == _kickPressed && from == _kickSelected) : (from == _kickSelected);
paintDialog(p, ms, _rows[from], data(from), selected, kickSelected);
p.translate(0, _rowHeight);
}
if (to == _rows.size() && _filter == MembersFilter::Recent && (_rows.size() < _channel->membersCount() || _rows.size() >= Global::ChatSizeMax())) {
p.setPen(st::membersAboutLimitFg);
_about.draw(p, st::contactsPadding.left(), st::membersAboutLimitPadding.top(), _aboutWidth, style::al_center);
}
}
}
void MembersBox::Inner::enterEventHook(QEvent *e) {
setMouseTracking(true);
}
void MembersBox::Inner::leaveEventHook(QEvent *e) {
_mouseSelection = false;
setMouseTracking(false);
if (_selected >= 0) {
clearSel();
}
}
void MembersBox::Inner::mouseMoveEvent(QMouseEvent *e) {
_mouseSelection = true;
_lastMousePos = e->globalPos();
updateSelection();
}
void MembersBox::Inner::mousePressEvent(QMouseEvent *e) {
_mouseSelection = true;
_lastMousePos = e->globalPos();
updateSelection();
setPressed(_selected);
_kickPressed = _kickSelected;
if (_selected >= 0 && _selected < _datas.size() && _kickSelected < 0) {
addRipple(_datas[_selected]);
}
}
void MembersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
auto pressed = _pressed;
auto kickPressed = _kickPressed;
setPressed(-1);
if (e->button() == Qt::LeftButton) {
if (pressed == _selected && kickPressed == _kickSelected) {
if (kickPressed >= 0) {
if (!_kickRequestId) {
_kickConfirm = _rows.at(_kickSelected);
if (_kickBox) _kickBox->deleteLater();
auto text = (_filter == MembersFilter::Recent ? (_channel->isMegagroup() ? lng_profile_sure_kick : lng_profile_sure_kick_channel) : lng_profile_sure_kick_admin)(lt_user, _kickConfirm->firstName);
_kickBox = Ui::show(Box<ConfirmBox>(text, base::lambda_guarded(this, [this] {
if (_filter == MembersFilter::Recent) {
_kickRequestId = MTP::send(MTPchannels_KickFromChannel(_channel->inputChannel, _kickConfirm->inputUser, MTP_bool(true)), rpcDone(&Inner::kickDone), rpcFail(&Inner::kickFail));
} else {
_kickRequestId = MTP::send(MTPchannels_EditAdmin(_channel->inputChannel, _kickConfirm->inputUser, MTP_channelRoleEmpty()), rpcDone(&Inner::kickAdminDone), rpcFail(&Inner::kickFail));
}
})), KeepOtherLayers);
}
} else if (pressed >= 0) {
chooseParticipant();
}
}
}
}
void MembersBox::Inner::addRipple(MemberData *data) {
auto rowTop = getSelectedRowTop();
if (!data->ripple) {
auto mask = Ui::RippleAnimation::rectMask(QSize(width(), _rowHeight));
data->ripple = std::make_unique<Ui::RippleAnimation>(st::contactsRipple, std::move(mask), [this, data] {
updateRowWithTop(data->rippleRowTop);
});
}
data->rippleRowTop = rowTop;
data->ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(0, rowTop));
}
void MembersBox::Inner::stopLastRipple(MemberData *data) {
if (data->ripple) {
data->ripple->lastStop();
}
}
void MembersBox::Inner::setPressed(int pressed) {
if (_pressed >= 0 && _pressed < _datas.size()) {
stopLastRipple(_datas[_pressed]);
}
_pressed = pressed;
}
void MembersBox::Inner::paintDialog(Painter &p, TimeMs ms, PeerData *peer, MemberData *data, bool selected, bool kickSelected) {
UserData *user = peer->asUser();
p.fillRect(0, 0, width(), _rowHeight, selected ? st::contactsBgOver : st::contactsBg);
if (data->ripple) {
data->ripple->paint(p, 0, 0, width(), ms);
if (data->ripple->empty()) {
data->ripple.reset();
}
}
peer->paintUserpicLeft(p, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize);
p.setPen(st::contactsNameFg);
int32 namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left();
int32 namew = width() - namex - st::contactsPadding.right() - (data->canKick ? (_kickWidth + st::contactsCheckPosition.x() * 2) : 0);
if (peer->isVerified()) {
auto icon = &st::dialogsVerifiedIcon;
namew -= icon->width();
icon->paint(p, namex + qMin(data->name.maxWidth(), namew), st::contactsPadding.top() + st::contactsNameTop, width());
}
data->name.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsNameTop, namew, width());
if (data->canKick) {
p.setFont(kickSelected ? st::linkOverFont : st::linkFont);
p.setPen(kickSelected ? st::defaultLinkButton.overColor : st::defaultLinkButton.color);
p.drawTextRight(st::contactsPadding.right() + st::contactsCheckPosition.x(), st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, width(), _kickText, _kickWidth);
}
p.setFont(st::contactsStatusFont->f);
p.setPen(data->onlineColor ? st::contactsStatusFgOnline : (selected ? st::contactsStatusFgOver : st::contactsStatusFg));
p.drawTextLeft(namex, st::contactsPadding.top() + st::contactsStatusTop, width(), data->online);
}
void MembersBox::Inner::selectSkip(int32 dir) {
_time = unixtime();
_mouseSelection = false;
int cur = -1;
if (_selected >= 0) {
cur = _selected;
}
cur += dir;
if (cur <= 0) {
_selected = _rows.isEmpty() ? -1 : 0;
} else if (cur >= _rows.size()) {
_selected = -1;
} else {
_selected = cur;
}
if (dir > 0) {
if (_selected < 0 || _selected >= _rows.size()) {
_selected = -1;
}
} else {
if (!_rows.isEmpty()) {
if (_selected < 0) _selected = _rows.size() - 1;
}
}
if (_selected >= 0) {
emit mustScrollTo(st::membersMarginTop + _selected * _rowHeight, st::membersMarginTop + (_selected + 1) * _rowHeight);
}
update();
}
void MembersBox::Inner::selectSkipPage(int32 h, int32 dir) {
int32 points = h / _rowHeight;
if (!points) return;
selectSkip(points * dir);
}
MembersBox::Inner::MemberData::MemberData() = default;
MembersBox::Inner::MemberData::~MemberData() = default;
void MembersBox::Inner::loadProfilePhotos() {
if (_visibleTop >= _visibleBottom) return;
auto yFrom = _visibleTop;
auto yTo = yFrom + (_visibleBottom - _visibleTop) * 5;
AuthSession::Current().downloader().clearPriorities();
if (yTo < 0) return;
if (yFrom < 0) yFrom = 0;
if (!_rows.isEmpty()) {
int32 from = yFrom / _rowHeight;
if (from < 0) from = 0;
if (from < _rows.size()) {
int32 to = (yTo / _rowHeight) + 1;
if (to > _rows.size()) to = _rows.size();
for (; from < to; ++from) {
_rows[from]->loadUserpic();
}
}
}
}
void MembersBox::Inner::chooseParticipant() {
if (_selected < 0 || _selected >= _rows.size()) return;
if (auto peer = _rows[_selected]) {
Ui::hideLayer();
Ui::showPeerProfile(peer);
}
}
void MembersBox::Inner::refresh() {
if (_rows.isEmpty()) {
resize(width(), st::membersMarginTop + st::noContactsHeight + st::membersMarginBottom);
_aboutHeight = 0;
} else {
_about.setText(st::boxLabelStyle, lng_channel_only_last_shown(lt_count, _rows.size()));
_aboutHeight = st::membersAboutLimitPadding.top() + _about.countHeight(_aboutWidth) + st::membersAboutLimitPadding.bottom();
if (_filter != MembersFilter::Recent || (_rows.size() >= _channel->membersCount() && _rows.size() < Global::ChatSizeMax())) {
_aboutHeight = 0;
}
resize(width(), st::membersMarginTop + _aboutHeight + _rows.size() * _rowHeight + st::membersMarginBottom);
}
update();
}
ChannelData *MembersBox::Inner::channel() const {
return _channel;
}
MembersFilter MembersBox::Inner::filter() const {
return _filter;
}
MembersAlreadyIn MembersBox::Inner::already() const {
MembersAlreadyIn result;
for_const (auto peer, _rows) {
if (peer->isUser()) {
result.insert(peer->asUser());
}
}
return result;
}
void MembersBox::Inner::setVisibleTopBottom(int visibleTop, int visibleBottom) {
_visibleTop = visibleTop;
_visibleBottom = visibleBottom;
loadProfilePhotos();
}
void MembersBox::Inner::clearSel() {
updateSelectedRow();
_selected = _kickSelected = -1;
_lastMousePos = QCursor::pos();
updateSelection();
}
MembersBox::Inner::MemberData *MembersBox::Inner::data(int32 index) {
if (MemberData *result = _datas.at(index)) {
return result;
}
MemberData *result = _datas[index] = new MemberData();
result->name.setText(st::contactsNameStyle, _rows[index]->name, _textNameOptions);
int32 t = unixtime();
result->online = App::onlineText(_rows[index], t);// lng_mediaview_date_time(lt_date, _dates[index].date().toString(qsl("dd.MM.yy")), lt_time, _dates[index].time().toString(cTimeFormat()));
result->onlineColor = App::onlineColorUse(_rows[index], t);
if (_filter == MembersFilter::Recent) {
result->canKick = (_channel->amCreator() || _channel->amEditor() || _channel->amModerator()) ? (_roles[index] == MemberRole::None) : false;
} else if (_filter == MembersFilter::Admins) {
result->canKick = _channel->amCreator() ? (_roles[index] == MemberRole::Editor || _roles[index] == MemberRole::Moderator) : false;
} else {
result->canKick = false;
}
return result;
}
void MembersBox::Inner::clear() {
for (int32 i = 0, l = _datas.size(); i < l; ++i) {
delete _datas.at(i);
}
_datas.clear();
_rows.clear();
_dates.clear();
_roles.clear();
if (_kickBox) _kickBox->deleteLater();
clearSel();
}
MembersBox::Inner::~Inner() {
clear();
}
void MembersBox::Inner::updateSelection() {
if (!_mouseSelection) return;
QPoint p(mapFromGlobal(_lastMousePos));
p.setY(p.y() - st::membersMarginTop);
bool in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePos));
auto selected = (in && p.y() >= 0 && p.y() < _rows.size() * _rowHeight) ? (p.y() / _rowHeight) : -1;
auto kickSelected = selected;
if (selected >= 0 && (!data(selected)->canKick || !QRect(width() - _kickWidth - st::contactsPadding.right() - st::contactsCheckPosition.x(), selected * _rowHeight + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _kickWidth, st::normalFont->height).contains(p))) {
kickSelected = -1;
}
if (_selected != selected || _kickSelected != kickSelected) {
updateSelectedRow();
_selected = selected;
_kickSelected = kickSelected;
updateSelectedRow();
setCursor(_kickSelected >= 0 ? style::cur_pointer : style::cur_default);
}
}
void MembersBox::Inner::peerUpdated(PeerData *peer) {
update();
}
int MembersBox::Inner::getSelectedRowTop() const {
if (_selected >= 0) {
return st::membersMarginTop + _selected * _rowHeight;
}
return -1;
}
void MembersBox::Inner::updateRowWithTop(int rowTop) {
update(0, rowTop, width(), _rowHeight);
}
void MembersBox::Inner::updateSelectedRow() {
auto rowTop = getSelectedRowTop();
if (rowTop >= 0) {
updateRowWithTop(rowTop);
}
}
void MembersBox::Inner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) {
for (int32 i = 0, l = _rows.size(); i < l; ++i) {
if (_rows.at(i) == peer) {
if (_datas.at(i)) {
_datas.at(i)->name.setText(st::contactsNameStyle, peer->name, _textNameOptions);
update(0, st::membersMarginTop + i * _rowHeight, width(), _rowHeight);
} else {
break;
}
}
}
}
void MembersBox::Inner::membersReceived(const MTPchannels_ChannelParticipants &result, mtpRequestId req) {
Expects(result.type() == mtpc_channels_channelParticipants);
clear();
_loadingRequestId = 0;
auto &d = result.c_channels_channelParticipants();
auto &v = d.vparticipants.v;
_rows.reserve(v.size());
_datas.reserve(v.size());
_dates.reserve(v.size());
_roles.reserve(v.size());
if (_filter == MembersFilter::Recent && _channel->membersCount() < d.vcount.v) {
_channel->setMembersCount(d.vcount.v);
if (App::main()) emit App::main()->peerUpdated(_channel);
} else if (_filter == MembersFilter::Admins && _channel->adminsCount() < d.vcount.v) {
_channel->setAdminsCount(d.vcount.v);
if (App::main()) emit App::main()->peerUpdated(_channel);
}
App::feedUsers(d.vusers);
for (QVector<MTPChannelParticipant>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
int32 userId = 0, addedTime = 0;
MemberRole role = MemberRole::None;
switch (i->type()) {
case mtpc_channelParticipant:
userId = i->c_channelParticipant().vuser_id.v;
addedTime = i->c_channelParticipant().vdate.v;
break;
case mtpc_channelParticipantSelf:
role = MemberRole::Self;
userId = i->c_channelParticipantSelf().vuser_id.v;
addedTime = i->c_channelParticipantSelf().vdate.v;
break;
case mtpc_channelParticipantModerator:
role = MemberRole::Moderator;
userId = i->c_channelParticipantModerator().vuser_id.v;
addedTime = i->c_channelParticipantModerator().vdate.v;
break;
case mtpc_channelParticipantEditor:
role = MemberRole::Editor;
userId = i->c_channelParticipantEditor().vuser_id.v;
addedTime = i->c_channelParticipantEditor().vdate.v;
break;
case mtpc_channelParticipantKicked:
userId = i->c_channelParticipantKicked().vuser_id.v;
addedTime = i->c_channelParticipantKicked().vdate.v;
role = MemberRole::Kicked;
break;
case mtpc_channelParticipantCreator:
userId = i->c_channelParticipantCreator().vuser_id.v;
addedTime = _channel->date;
role = MemberRole::Creator;
break;
}
if (UserData *user = App::userLoaded(userId)) {
_rows.push_back(user);
_dates.push_back(date(addedTime));
_roles.push_back(role);
_datas.push_back(0);
}
}
// update admins if we got all of them
if (_filter == MembersFilter::Admins && _channel->isMegagroup() && _rows.size() < Global::ChatSizeMax()) {
_channel->mgInfo->lastAdmins.clear();
for (int32 i = 0, l = _rows.size(); i != l; ++i) {
if (_roles.at(i) == MemberRole::Creator || _roles.at(i) == MemberRole::Editor) {
_channel->mgInfo->lastAdmins.insert(_rows.at(i));
}
}
Notify::peerUpdatedDelayed(_channel, Notify::PeerUpdate::Flag::AdminsChanged);
}
if (_rows.isEmpty()) {
_rows.push_back(App::self());
_dates.push_back(date(MTP_int(_channel->date)));
_roles.push_back(MemberRole::Self);
_datas.push_back(0);
}
clearSel();
_loading = false;
refresh();
emit loaded();
}
bool MembersBox::Inner::membersFailed(const RPCError &error, mtpRequestId req) {
if (MTP::isDefaultHandledError(error)) return false;
Ui::hideLayer();
return true;
}
void MembersBox::Inner::kickDone(const MTPUpdates &result, mtpRequestId req) {
App::main()->sentUpdatesReceived(result);
if (_kickRequestId != req) return;
removeKicked();
if (_kickBox) _kickBox->closeBox();
}
void MembersBox::Inner::kickAdminDone(const MTPUpdates &result, mtpRequestId req) {
if (_kickRequestId != req) return;
if (App::main()) App::main()->sentUpdatesReceived(result);
removeKicked();
if (_kickBox) _kickBox->closeBox();
}
bool MembersBox::Inner::kickFail(const RPCError &error, mtpRequestId req) {
if (MTP::isDefaultHandledError(error)) return false;
if (_kickBox) _kickBox->closeBox();
load();
return true;
}
void MembersBox::Inner::removeKicked() {
_kickRequestId = 0;
int32 index = _rows.indexOf(_kickConfirm);
if (index >= 0) {
_rows.removeAt(index);
delete _datas.at(index);
_datas.removeAt(index);
_dates.removeAt(index);
_roles.removeAt(index);
clearSel();
if (_filter == MembersFilter::Recent && _channel->membersCount() > 1) {
_channel->setMembersCount(_channel->membersCount() - 1);
if (App::main()) emit App::main()->peerUpdated(_channel);
} else if (_filter == MembersFilter::Admins && _channel->adminsCount() > 1) {
_channel->setAdminsCount(_channel->adminsCount() - 1);
if (App::main()) emit App::main()->peerUpdated(_channel);
}
refresh();
}
_kickConfirm = 0;
}

View File

@@ -1,213 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "boxes/abstract_box.h"
#include "core/single_timer.h"
#include "ui/effects/round_checkbox.h"
#include "ui/widgets/buttons.h"
class ContactsBox;
class ConfirmBox;
enum class MembersFilter {
Recent,
Admins,
};
using MembersAlreadyIn = OrderedSet<UserData*>;
// Not used for now.
//
//class MembersAddButton : public Ui::RippleButton {
//public:
// MembersAddButton(QWidget *parent, const style::TwoIconButton &st);
//
//protected:
// void paintEvent(QPaintEvent *e) override;
//
// QImage prepareRippleMask() const override;
// QPoint prepareRippleStartPosition() const override;
//
//private:
// const style::TwoIconButton &_st;
//
//};
class MembersBox : public BoxContent {
Q_OBJECT
public:
MembersBox(QWidget*, ChannelData *channel, MembersFilter filter);
public slots:
void onAdminAdded();
protected:
void prepare() override;
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
void onAdd();
ChannelData *_channel = nullptr;
MembersFilter _filter = MembersFilter::Recent;
class Inner;
QPointer<Inner> _inner;
QPointer<ContactsBox> _addBox;
object_ptr<SingleTimer> _loadTimer = { nullptr };
};
// This class is hold in header because it requires Qt preprocessing.
class MembersBox::Inner : public TWidget, public RPCSender, private base::Subscriber {
Q_OBJECT
public:
Inner(QWidget *parent, ChannelData *channel, MembersFilter filter);
void selectSkip(int32 dir);
void selectSkipPage(int32 h, int32 dir);
void chooseParticipant();
void refresh();
ChannelData *channel() const;
MembersFilter filter() const;
bool isLoaded() const {
return !_loading;
}
void clearSel();
MembersAlreadyIn already() const;
void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
~Inner();
signals:
void mustScrollTo(int ymin, int ymax);
void loaded();
public slots:
void load();
void peerUpdated(PeerData *peer);
void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars);
protected:
void paintEvent(QPaintEvent *e) override;
void enterEventHook(QEvent *e) override;
void leaveEventHook(QEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
private:
struct MemberData {
MemberData();
~MemberData();
std::unique_ptr<Ui::RippleAnimation> ripple;
int rippleRowTop = 0;
Text name;
QString online;
bool onlineColor;
bool canKick;
};
void addRipple(MemberData *data);
void stopLastRipple(MemberData *data);
void setPressed(int pressed);
void updateSelection();
void loadProfilePhotos();
void updateRowWithTop(int rowTop);
int getSelectedRowTop() const;
void updateSelectedRow();
MemberData *data(int32 index);
void paintDialog(Painter &p, TimeMs ms, PeerData *peer, MemberData *data, bool selected, bool kickSelected);
void membersReceived(const MTPchannels_ChannelParticipants &result, mtpRequestId req);
bool membersFailed(const RPCError &error, mtpRequestId req);
void kickDone(const MTPUpdates &result, mtpRequestId req);
void kickAdminDone(const MTPUpdates &result, mtpRequestId req);
bool kickFail(const RPCError &error, mtpRequestId req);
void removeKicked();
void clear();
int _rowHeight = 0;
int _visibleTop = 0;
int _visibleBottom = 0;
ChannelData *_channel = nullptr;
MembersFilter _filter;
QString _kickText;
TimeId _time = 0;
int _kickWidth = 0;
int _selected = -1;
int _pressed = -1;
int _kickSelected = -1;
int _kickPressed = -1;
bool _mouseSelection = false;
UserData *_kickConfirm = nullptr;
mtpRequestId _kickRequestId = 0;
QPointer<ConfirmBox> _kickBox;
enum class MemberRole {
None,
Self,
Creator,
Editor,
Moderator,
Kicked
};
bool _loading = true;
mtpRequestId _loadingRequestId = 0;
typedef QVector<UserData*> MemberRows;
typedef QVector<QDateTime> MemberDates;
typedef QVector<MemberRole> MemberRoles;
typedef QVector<MemberData*> MemberDatas;
MemberRows _rows;
MemberDates _dates;
MemberRoles _roles;
MemberDatas _datas;
int _aboutWidth = 0;
Text _about;
int _aboutHeight = 0;
QPoint _lastMousePos;
};

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/notifications_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/discrete_sliders.h"
#include "styles/style_boxes.h"
@@ -119,7 +119,7 @@ NotificationsBox::NotificationsBox(QWidget *parent)
}
void NotificationsBox::prepare() {
addButton(lang(lng_close), [this] { closeBox(); });
addButton(langFactory(lng_close), [this] { closeBox(); });
_sampleOpacities.reserve(kMaxNotificationsCount);
for (int i = 0; i != kMaxNotificationsCount; ++i) {

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/passcode_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "boxes/confirm_box.h"
#include "mainwindow.h"
#include "storage/localstorage.h"
@@ -31,11 +31,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
: _turningOff(turningOff)
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
, _oldPasscode(this, st::defaultInputField, lang(lng_passcode_enter_old))
, _newPasscode(this, st::defaultInputField, lang(Global::LocalPasscode() ? lng_passcode_enter_new : lng_passcode_enter_first))
, _reenterPasscode(this, st::defaultInputField, lang(lng_passcode_confirm_new))
, _passwordHint(this, st::defaultInputField, lang(lng_cloud_password_hint))
, _recoverEmail(this, st::defaultInputField, lang(lng_cloud_password_email))
, _oldPasscode(this, st::defaultInputField, langFactory(lng_passcode_enter_old))
, _newPasscode(this, st::defaultInputField, langFactory(Global::LocalPasscode() ? lng_passcode_enter_new : lng_passcode_enter_first))
, _reenterPasscode(this, st::defaultInputField, langFactory(lng_passcode_confirm_new))
, _passwordHint(this, st::defaultInputField, langFactory(lng_cloud_password_hint))
, _recoverEmail(this, st::defaultInputField, langFactory(lng_cloud_password_email))
, _recover(this, lang(lng_signin_recover)) {
}
@@ -46,34 +46,34 @@ PasscodeBox::PasscodeBox(QWidget*, const QByteArray &newSalt, const QByteArray &
, _curSalt(curSalt)
, _hasRecovery(hasRecovery)
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
, _oldPasscode(this, st::defaultInputField, lang(lng_cloud_password_enter_old))
, _newPasscode(this, st::defaultInputField, lang(curSalt.isEmpty() ? lng_cloud_password_enter_first : lng_cloud_password_enter_new))
, _reenterPasscode(this, st::defaultInputField, lang(lng_cloud_password_confirm_new))
, _passwordHint(this, st::defaultInputField, lang(curSalt.isEmpty() ? lng_cloud_password_hint : lng_cloud_password_change_hint))
, _recoverEmail(this, st::defaultInputField, lang(lng_cloud_password_email))
, _oldPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_enter_old))
, _newPasscode(this, st::defaultInputField, langFactory(curSalt.isEmpty() ? lng_cloud_password_enter_first : lng_cloud_password_enter_new))
, _reenterPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_confirm_new))
, _passwordHint(this, st::defaultInputField, langFactory(curSalt.isEmpty() ? lng_cloud_password_hint : lng_cloud_password_change_hint))
, _recoverEmail(this, st::defaultInputField, langFactory(lng_cloud_password_email))
, _recover(this, lang(lng_signin_recover)) {
if (!hint.isEmpty()) _hintText.setText(st::passcodeTextStyle, lng_signin_hint(lt_password_hint, hint));
}
void PasscodeBox::prepare() {
addButton(lang(_turningOff ? lng_passcode_remove_button : lng_settings_save), [this] { onSave(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(_turningOff ? lng_passcode_remove_button : lng_settings_save), [this] { onSave(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
_about.setRichText(st::passcodeTextStyle, lang(_cloudPwd ? lng_cloud_password_about : lng_passcode_about));
_aboutHeight = _about.countHeight(st::boxWidth - st::boxPadding.left() * 1.5);
if (_turningOff) {
_oldPasscode->show();
setTitle(lang(_cloudPwd ? lng_cloud_password_remove : lng_passcode_remove));
setTitle(langFactory(_cloudPwd ? lng_cloud_password_remove : lng_passcode_remove));
setDimensions(st::boxWidth, st::passcodePadding.top() + _oldPasscode->height() + st::passcodeTextLine + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeTextLine : 0) + st::passcodeAboutSkip + _aboutHeight + st::passcodePadding.bottom());
} else {
auto has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode();
if (has) {
_oldPasscode->show();
setTitle(lang(_cloudPwd ? lng_cloud_password_change : lng_passcode_change));
setTitle(langFactory(_cloudPwd ? lng_cloud_password_change : lng_passcode_change));
setDimensions(st::boxWidth, st::passcodePadding.top() + _oldPasscode->height() + st::passcodeTextLine + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeTextLine : 0) + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + st::passcodePadding.bottom());
} else {
_oldPasscode->hide();
setTitle(lang(_cloudPwd ? lng_cloud_password_create : lng_passcode_create));
setTitle(langFactory(_cloudPwd ? lng_cloud_password_create : lng_passcode_create));
setDimensions(st::boxWidth, st::passcodePadding.top() + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + (_cloudPwd ? (st::passcodeLittleSkip + _recoverEmail->height() + st::passcodeSkip) : st::passcodePadding.bottom()));
}
}
@@ -421,14 +421,14 @@ bool PasscodeBox::recoverStartFail(const RPCError &error) {
RecoverBox::RecoverBox(QWidget*, const QString &pattern)
: _pattern(st::normalFont->elided(lng_signin_recover_hint(lt_recover_email, pattern), st::boxWidth - st::boxPadding.left() * 1.5))
, _recoverCode(this, st::defaultInputField, lang(lng_signin_code)) {
, _recoverCode(this, st::defaultInputField, langFactory(lng_signin_code)) {
}
void RecoverBox::prepare() {
setTitle(lang(lng_signin_recover_title));
setTitle(langFactory(lng_signin_recover_title));
addButton(lang(lng_passcode_submit), [this] { onSubmit(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_passcode_submit), [this] { onSubmit(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
setDimensions(st::boxWidth, st::passcodePadding.top() + st::passcodePadding.bottom() + st::passcodeTextLine + _recoverCode->height() + st::passcodeTextLine);

File diff suppressed because it is too large Load Diff

View File

@@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "boxes/abstract_box.h"
#include "mtproto/sender.h"
#include "base/timer.h"
namespace Ui {
class RippleAnimation;
@@ -32,216 +33,305 @@ class WidgetSlideWrap;
class FlatLabel;
} // namespace Ui
class PeerListBox : public BoxContent {
class Inner;
using PeerListRowId = uint64;
class PeerListRow {
public:
using RowId = uint64;
PeerListRow(gsl::not_null<PeerData*> peer);
PeerListRow(gsl::not_null<PeerData*> peer, PeerListRowId id);
class Row {
public:
Row(PeerData *peer);
Row(PeerData *peer, RowId id);
void setDisabled(bool disabled) {
_disabled = disabled;
}
void setDisabled(bool disabled) {
_disabled = disabled;
}
// Checked state is controlled by the box with multiselect,
// not by the row itself, so there is no setChecked() method.
// We can query the checked state from row, but before it is
// added to the box it is always false.
bool checked() const;
// Checked state is controlled by the box with multiselect,
// not by the row itself, so there is no setChecked() method.
// We can query the checked state from row, but before it is
// added to the box it is always false.
bool checked() const;
gsl::not_null<PeerData*> peer() const {
return _peer;
}
PeerListRowId id() const {
return _id;
}
PeerData *peer() const {
return _peer;
}
RowId id() const {
return _id;
}
void setCustomStatus(const QString &status);
void clearCustomStatus();
void setCustomStatus(const QString &status);
void clearCustomStatus();
virtual ~PeerListRow();
virtual ~Row();
// Box interface.
virtual bool needsVerifiedIcon() const {
return _peer->isVerified();
}
virtual QSize actionSize() const {
return QSize();
}
virtual QMargins actionMargins() const {
return QMargins();
}
virtual void addActionRipple(QPoint point, base::lambda<void()> updateCallback) {
}
virtual void stopLastActionRipple() {
}
virtual void paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) {
}
protected:
virtual void paintStatusText(Painter &p, int x, int y, int outerWidth, bool selected);
bool isInitialized() const {
return _initialized;
}
virtual void lazyInitialize();
private:
// Inner interface.
friend class PeerListBox;
friend class Inner;
virtual bool needsVerifiedIcon() const {
return _peer->isVerified();
}
virtual QSize actionSize() const {
return QSize();
}
virtual QMargins actionMargins() const {
return QMargins();
}
virtual void addActionRipple(QPoint point, base::lambda<void()> updateCallback) {
}
virtual void stopLastActionRipple() {
}
virtual void paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) {
}
void refreshName();
const Text &name() const {
return _name;
}
enum class StatusType {
Online,
LastSeen,
Custom,
};
void refreshStatus();
void setAbsoluteIndex(int index) {
_absoluteIndex = index;
}
int absoluteIndex() const {
return _absoluteIndex;
}
bool disabled() const {
return _disabled;
}
bool isGlobalSearchResult() const {
return _isGlobalSearchResult;
}
void setIsGlobalSearchResult(bool isGlobalSearchResult) {
_isGlobalSearchResult = isGlobalSearchResult;
}
enum class SetStyle {
Animated,
Fast,
};
template <typename UpdateCallback>
void setChecked(bool checked, SetStyle style, UpdateCallback callback) {
if (checked && !_checkbox) {
createCheckbox(std::move(callback));
}
setCheckedInternal(checked, style);
}
void invalidatePixmapsCache();
template <typename UpdateCallback>
void addRipple(QSize size, QPoint point, UpdateCallback updateCallback);
void stopLastRipple();
void paintRipple(Painter &p, TimeMs ms, int x, int y, int outerWidth);
void paintUserpic(Painter &p, TimeMs ms, int x, int y, int outerWidth);
float64 checkedRatio();
void setNameFirstChars(const OrderedSet<QChar> &nameFirstChars) {
_nameFirstChars = nameFirstChars;
}
const OrderedSet<QChar> &nameFirstChars() const {
return _nameFirstChars;
}
private:
void createCheckbox(base::lambda<void()> updateCallback);
void setCheckedInternal(bool checked, SetStyle style);
void paintDisabledCheckUserpic(Painter &p, int x, int y, int outerWidth) const;
RowId _id = 0;
PeerData *_peer = nullptr;
bool _initialized = false;
std::unique_ptr<Ui::RippleAnimation> _ripple;
std::unique_ptr<Ui::RoundImageCheckbox> _checkbox;
Text _name;
QString _status;
StatusType _statusType = StatusType::Online;
bool _disabled = false;
int _absoluteIndex = -1;
OrderedSet<QChar> _nameFirstChars;
bool _isGlobalSearchResult = false;
void refreshName();
const Text &name() const {
return _name;
}
enum class StatusType {
Online,
LastSeen,
Custom,
};
void refreshStatus();
class Controller {
public:
virtual void prepare() = 0;
virtual void rowClicked(Row *row) = 0;
virtual void rowActionClicked(Row *row) {
}
virtual void preloadRows() {
}
virtual std::unique_ptr<Row> createGlobalRow(PeerData *peer) {
return std::unique_ptr<Row>();
}
virtual ~Controller() = default;
protected:
PeerListBox *view() const {
return _view;
}
private:
void setView(PeerListBox *box) {
_view = box;
prepare();
}
PeerListBox *_view = nullptr;
friend class PeerListBox;
friend class Inner;
void setAbsoluteIndex(int index) {
_absoluteIndex = index;
}
int absoluteIndex() const {
return _absoluteIndex;
}
bool disabled() const {
return _disabled;
}
bool isSearchResult() const {
return _isSearchResult;
}
void setIsSearchResult(bool isSearchResult) {
_isSearchResult = isSearchResult;
}
enum class SetStyle {
Animated,
Fast,
};
PeerListBox(QWidget*, std::unique_ptr<Controller> controller);
template <typename UpdateCallback>
void setChecked(bool checked, SetStyle style, UpdateCallback callback) {
if (checked && !_checkbox) {
createCheckbox(std::move(callback));
}
setCheckedInternal(checked, style);
}
void invalidatePixmapsCache();
// Interface for the controller.
void appendRow(std::unique_ptr<Row> row);
void prependRow(std::unique_ptr<Row> row);
Row *findRow(RowId id);
void updateRow(Row *row);
void removeRow(Row *row);
void setRowChecked(Row *row, bool checked);
int fullRowsCount() const;
Row *rowAt(int index) const;
void setAboutText(const QString &aboutText);
void setAbout(object_ptr<Ui::FlatLabel> about);
void refreshRows();
template <typename UpdateCallback>
void addRipple(QSize size, QPoint point, UpdateCallback updateCallback);
void stopLastRipple();
void paintRipple(Painter &p, TimeMs ms, int x, int y, int outerWidth);
void paintUserpic(Painter &p, TimeMs ms, int x, int y, int outerWidth);
float64 checkedRatio();
// Search works only with RowId == peer->id.
enum class SearchMode {
None,
Local,
Global,
};
void setSearchMode(SearchMode mode);
void setSearchNoResultsText(const QString &noResultsText);
void setSearchNoResults(object_ptr<Ui::FlatLabel> searchNoResults);
void setSearchLoadingText(const QString &searchLoadingText);
void setSearchLoading(object_ptr<Ui::FlatLabel> searchLoading);
void setNameFirstChars(const OrderedSet<QChar> &nameFirstChars) {
_nameFirstChars = nameFirstChars;
}
const OrderedSet<QChar> &nameFirstChars() const {
return _nameFirstChars;
}
virtual void lazyInitialize();
virtual void paintStatusText(Painter &p, int x, int y, int availableWidth, int outerWidth, bool selected);
protected:
bool isInitialized() const {
return _initialized;
}
private:
void createCheckbox(base::lambda<void()> updateCallback);
void setCheckedInternal(bool checked, SetStyle style);
void paintDisabledCheckUserpic(Painter &p, int x, int y, int outerWidth) const;
void setStatusText(const QString &text);
PeerListRowId _id = 0;
gsl::not_null<PeerData*> _peer;
std::unique_ptr<Ui::RippleAnimation> _ripple;
std::unique_ptr<Ui::RoundImageCheckbox> _checkbox;
Text _name;
Text _status;
StatusType _statusType = StatusType::Online;
OrderedSet<QChar> _nameFirstChars;
int _absoluteIndex = -1;
bool _initialized : 1;
bool _disabled : 1;
bool _isSearchResult : 1;
};
enum class PeerListSearchMode {
Disabled,
Enabled,
};
class PeerListDelegate {
public:
virtual void peerListSetTitle(base::lambda<QString()> title) = 0;
virtual void peerListSetDescription(object_ptr<Ui::FlatLabel> description) = 0;
virtual void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) = 0;
virtual void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) = 0;
virtual void peerListSetSearchMode(PeerListSearchMode mode) = 0;
virtual void peerListAppendRow(std::unique_ptr<PeerListRow> row) = 0;
virtual void peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) = 0;
virtual void peerListAppendFoundRow(gsl::not_null<PeerListRow*> row) = 0;
virtual void peerListPrependRow(std::unique_ptr<PeerListRow> row) = 0;
virtual void peerListPrependRowFromSearchResult(gsl::not_null<PeerListRow*> row) = 0;
virtual void peerListUpdateRow(gsl::not_null<PeerListRow*> row) = 0;
virtual void peerListRemoveRow(gsl::not_null<PeerListRow*> row) = 0;
virtual void peerListConvertRowToSearchResult(gsl::not_null<PeerListRow*> row) = 0;
virtual bool peerListIsRowSelected(gsl::not_null<PeerData*> peer) = 0;
virtual void peerListSetRowChecked(gsl::not_null<PeerListRow*> row, bool checked) = 0;
virtual gsl::not_null<PeerListRow*> peerListRowAt(int index) = 0;
virtual void peerListRefreshRows() = 0;
virtual void peerListScrollToTop() = 0;
virtual int peerListFullRowsCount() = 0;
virtual PeerListRow *peerListFindRow(PeerListRowId id) = 0;
virtual void peerListSortRows(base::lambda<bool(PeerListRow &a, PeerListRow &b)> compare) = 0;
virtual void peerListPartitionRows(base::lambda<bool(PeerListRow &a)> border) = 0;
template <typename PeerDataRange>
void addSelectedRows(PeerDataRange &&range) {
Expects(_select != nullptr);
void peerListAddSelectedRows(PeerDataRange &&range) {
for (auto peer : range) {
addSelectItem(peer, Row::SetStyle::Fast);
peerListAddSelectedRowInBunch(peer);
}
finishSelectItemsBunch();
peerListFinishSelectedRowsBunch();
}
QVector<PeerData*> collectSelectedRows() const;
// callback takes two iterators, like [](auto &begin, auto &end).
template <typename ReorderCallback>
void reorderRows(ReorderCallback &&callback);
virtual std::vector<gsl::not_null<PeerData*>> peerListCollectSelectedRows() = 0;
virtual ~PeerListDelegate() = default;
bool isRowSelected(PeerData *peer) const;
private:
virtual void peerListAddSelectedRowInBunch(gsl::not_null<PeerData*> peer) = 0;
virtual void peerListFinishSelectedRowsBunch() = 0;
};
class PeerListSearchDelegate {
public:
virtual void peerListSearchAddRow(gsl::not_null<PeerData*> peer) = 0;
virtual void peerListSearchRefreshRows() = 0;
virtual ~PeerListSearchDelegate() = default;
};
class PeerListSearchController {
public:
virtual void searchQuery(const QString &query) = 0;
virtual bool isLoading() = 0;
virtual bool loadMoreRows() = 0;
virtual ~PeerListSearchController() = default;
void setDelegate(gsl::not_null<PeerListSearchDelegate*> delegate) {
_delegate = delegate;
}
protected:
gsl::not_null<PeerListSearchDelegate*> delegate() const {
return _delegate;
}
private:
PeerListSearchDelegate *_delegate = nullptr;
};
class PeerListController : public PeerListSearchDelegate {
public:
// Search works only with RowId == peer->id.
PeerListController(std::unique_ptr<PeerListSearchController> searchController = nullptr);
void setDelegate(gsl::not_null<PeerListDelegate*> delegate) {
_delegate = delegate;
prepare();
}
virtual void prepare() = 0;
virtual void rowClicked(gsl::not_null<PeerListRow*> row) = 0;
virtual void rowActionClicked(gsl::not_null<PeerListRow*> row) {
}
virtual void loadMoreRows() {
}
bool isSearchLoading() const {
return _searchController ? _searchController->isLoading() : false;
}
virtual std::unique_ptr<PeerListRow> createSearchRow(gsl::not_null<PeerData*> peer) {
return std::unique_ptr<PeerListRow>();
}
bool isRowSelected(gsl::not_null<PeerData*> peer) {
return delegate()->peerListIsRowSelected(peer);
}
virtual bool searchInLocal() {
return true;
}
bool hasComplexSearch() const;
void search(const QString &query);
void peerListSearchAddRow(gsl::not_null<PeerData*> peer) override;
void peerListSearchRefreshRows() override;
virtual ~PeerListController() = default;
protected:
gsl::not_null<PeerListDelegate*> delegate() const {
return _delegate;
}
PeerListSearchController *searchController() const {
return _searchController.get();
}
void setDescriptionText(const QString &text);
void setSearchLoadingText(const QString &text);
void setSearchNoResultsText(const QString &text);
void setDescription(object_ptr<Ui::FlatLabel> description) {
delegate()->peerListSetDescription(std::move(description));
}
void setSearchLoading(object_ptr<Ui::FlatLabel> loading) {
delegate()->peerListSetSearchLoading(std::move(loading));
}
void setSearchNoResults(object_ptr<Ui::FlatLabel> noResults) {
delegate()->peerListSetSearchNoResults(std::move(noResults));
}
private:
PeerListDelegate *_delegate = nullptr;
std::unique_ptr<PeerListSearchController> _searchController = nullptr;
};
class PeerListBox : public BoxContent, public PeerListDelegate {
public:
PeerListBox(QWidget*, std::unique_ptr<PeerListController> controller, base::lambda<void(PeerListBox*)> init);
void peerListSetTitle(base::lambda<QString()> title) override {
setTitle(std::move(title));
}
void peerListSetDescription(object_ptr<Ui::FlatLabel> description) override;
void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) override;
void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) override;
void peerListSetSearchMode(PeerListSearchMode mode) override;
void peerListAppendRow(std::unique_ptr<PeerListRow> row) override;
void peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) override;
void peerListAppendFoundRow(gsl::not_null<PeerListRow*> row) override;
void peerListPrependRow(std::unique_ptr<PeerListRow> row) override;
void peerListPrependRowFromSearchResult(gsl::not_null<PeerListRow*> row) override;
void peerListUpdateRow(gsl::not_null<PeerListRow*> row) override;
void peerListRemoveRow(gsl::not_null<PeerListRow*> row) override;
void peerListConvertRowToSearchResult(gsl::not_null<PeerListRow*> row) override;
void peerListSetRowChecked(gsl::not_null<PeerListRow*> row, bool checked) override;
gsl::not_null<PeerListRow*> peerListRowAt(int index) override;
bool peerListIsRowSelected(gsl::not_null<PeerData*> peer) override;
std::vector<gsl::not_null<PeerData*>> peerListCollectSelectedRows() override;
void peerListRefreshRows() override;
void peerListScrollToTop() override;
int peerListFullRowsCount() override;
PeerListRow *peerListFindRow(PeerListRowId id) override;
void peerListSortRows(base::lambda<bool(PeerListRow &a, PeerListRow &b)> compare) override;
void peerListPartitionRows(base::lambda<bool(PeerListRow &a)> border) override;
protected:
void prepare() override;
@@ -249,10 +339,15 @@ protected:
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private:
void addSelectItem(PeerData *peer, Row::SetStyle style);
void finishSelectItemsBunch();
void peerListAddSelectedRowInBunch(gsl::not_null<PeerData*> peer) override {
addSelectItem(peer, PeerListRow::SetStyle::Fast);
}
void peerListFinishSelectedRowsBunch() override;
void addSelectItem(gsl::not_null<PeerData*> peer, PeerListRow::SetStyle style);
object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>> createMultiSelect();
int getTopScrollSkip() const;
void updateScrollSkips();
@@ -260,18 +355,20 @@ private:
object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>> _select = { nullptr };
class Inner;
QPointer<Inner> _inner;
std::unique_ptr<Controller> _controller;
std::unique_ptr<PeerListController> _controller;
base::lambda<void(PeerListBox*)> _init;
};
// This class is hold in header because it requires Qt preprocessing.
class PeerListBox::Inner : public TWidget, private MTP::Sender, private base::Subscriber {
class PeerListBox::Inner : public TWidget, private base::Subscriber {
Q_OBJECT
public:
Inner(QWidget *parent, Controller *controller);
Inner(QWidget *parent, gsl::not_null<PeerListController*> controller);
void selectSkip(int direction);
void selectSkipPage(int height, int direction);
@@ -284,22 +381,26 @@ public:
void submitted();
// Interface for the controller.
void appendRow(std::unique_ptr<Row> row);
void prependRow(std::unique_ptr<Row> row);
Row *findRow(RowId id);
void updateRow(Row *row) {
void appendRow(std::unique_ptr<PeerListRow> row);
void appendSearchRow(std::unique_ptr<PeerListRow> row);
void appendFoundRow(gsl::not_null<PeerListRow*> row);
void prependRow(std::unique_ptr<PeerListRow> row);
void prependRowFromSearchResult(gsl::not_null<PeerListRow*> row);
PeerListRow *findRow(PeerListRowId id);
void updateRow(gsl::not_null<PeerListRow*> row) {
updateRow(row, RowIndex());
}
void removeRow(Row *row);
void removeRow(gsl::not_null<PeerListRow*> row);
void convertRowToSearchResult(gsl::not_null<PeerListRow*> row);
int fullRowsCount() const;
Row *rowAt(int index) const;
void setAbout(object_ptr<Ui::FlatLabel> about);
gsl::not_null<PeerListRow*> rowAt(int index) const;
void setDescription(object_ptr<Ui::FlatLabel> description);
void setSearchLoading(object_ptr<Ui::FlatLabel> loading);
void setSearchNoResults(object_ptr<Ui::FlatLabel> noResults);
void refreshRows();
void setSearchMode(SearchMode mode);
void setSearchNoResults(object_ptr<Ui::FlatLabel> searchNoResults);
void setSearchLoading(object_ptr<Ui::FlatLabel> searchLoading);
void changeCheckState(Row *row, bool checked, Row::SetStyle style);
void setSearchMode(PeerListSearchMode mode);
void changeCheckState(gsl::not_null<PeerListRow*> row, bool checked, PeerListRow::SetStyle style);
template <typename ReorderCallback>
void reorderRows(ReorderCallback &&callback) {
@@ -327,7 +428,7 @@ protected:
private:
void refreshIndices();
void appendGlobalSearchRow(std::unique_ptr<Row> row);
void removeRowAtIndex(std::vector<std::unique_ptr<PeerListRow>> &from, int index);
void invalidatePixmapsCache();
@@ -370,19 +471,20 @@ private:
void loadProfilePhotos();
void checkScrollForPreload();
void updateRow(Row *row, RowIndex hint);
void updateRow(gsl::not_null<PeerListRow*> row, RowIndex hint);
void updateRow(RowIndex row);
int getRowTop(RowIndex row) const;
Row *getRow(RowIndex element);
RowIndex findRowIndex(Row *row, RowIndex hint = RowIndex());
QRect getActionRect(Row *row, RowIndex index) const;
PeerListRow *getRow(RowIndex element);
RowIndex findRowIndex(gsl::not_null<PeerListRow*> row, RowIndex hint = RowIndex());
QRect getActionRect(gsl::not_null<PeerListRow*> row, RowIndex index) const;
void paintRow(Painter &p, TimeMs ms, RowIndex index);
void addRowEntry(Row *row);
void addToSearchIndex(Row *row);
void addRowEntry(gsl::not_null<PeerListRow*> row);
void addToSearchIndex(gsl::not_null<PeerListRow*> row);
bool addingToSearchIndex() const;
void removeFromSearchIndex(Row *row);
void removeFromSearchIndex(gsl::not_null<PeerListRow*> row);
void setSearchQuery(const QString &query, const QString &normalizedQuery);
bool showingSearch() const {
return !_searchQuery.isEmpty();
}
@@ -396,14 +498,11 @@ private:
int labelHeight() const;
void needGlobalSearch();
bool globalSearchInCache();
void globalSearchOnServer();
void globalSearchDone(const MTPcontacts_Found &result, mtpRequestId requestId);
bool globalSearchLoading() const;
void clearGlobalSearchRows();
void clearSearchRows();
gsl::not_null<PeerListController*> _controller;
PeerListSearchMode _searchMode = PeerListSearchMode::Disabled;
Controller *_controller = nullptr;
int _rowHeight = 0;
int _visibleTop = 0;
int _visibleBottom = 0;
@@ -412,43 +511,32 @@ private:
Selected _pressed;
bool _mouseSelection = false;
std::vector<std::unique_ptr<Row>> _rows;
std::map<RowId, Row*> _rowsById;
std::map<PeerData*, std::vector<Row*>> _rowsByPeer;
std::vector<std::unique_ptr<PeerListRow>> _rows;
std::map<PeerListRowId, gsl::not_null<PeerListRow*>> _rowsById;
std::map<PeerData*, std::vector<gsl::not_null<PeerListRow*>>> _rowsByPeer;
SearchMode _searchMode = SearchMode::None;
std::map<QChar, std::vector<Row*>> _searchIndex;
std::map<QChar, std::vector<gsl::not_null<PeerListRow*>>> _searchIndex;
QString _searchQuery;
std::vector<Row*> _filterResults;
QString _normalizedSearchQuery;
QString _mentionHighlight;
std::vector<gsl::not_null<PeerListRow*>> _filterResults;
object_ptr<Ui::FlatLabel> _about = { nullptr };
object_ptr<Ui::FlatLabel> _description = { nullptr };
object_ptr<Ui::FlatLabel> _searchNoResults = { nullptr };
object_ptr<Ui::FlatLabel> _searchLoading = { nullptr };
QPoint _lastMousePosition;
std::vector<std::unique_ptr<Row>> _globalSearchRows;
object_ptr<SingleTimer> _globalSearchTimer = { nullptr };
QString _globalSearchQuery;
QString _globalSearchHighlight;
mtpRequestId _globalSearchRequestId = 0;
std::map<QString, MTPcontacts_Found> _globalSearchCache;
std::map<mtpRequestId, QString> _globalSearchQueries;
std::vector<std::unique_ptr<PeerListRow>> _searchRows;
};
template <typename ReorderCallback>
inline void PeerListBox::reorderRows(ReorderCallback &&callback) {
_inner->reorderRows(std::forward<ReorderCallback>(callback));
}
class PeerListRowWithLink : public PeerListBox::Row {
class PeerListRowWithLink : public PeerListRow {
public:
using Row::Row;
using PeerListRow::PeerListRow;
void setActionLink(const QString &action);
protected:
void lazyInitialize() override;
private:
@@ -462,25 +550,50 @@ private:
};
class ChatsListBoxController : public PeerListBox::Controller, protected base::Subscriber {
class PeerListGlobalSearchController : public PeerListSearchController, private MTP::Sender {
public:
PeerListGlobalSearchController();
void searchQuery(const QString &query) override;
bool isLoading() override;
bool loadMoreRows() override {
return false;
}
private:
bool searchInCache();
void searchOnServer();
void searchDone(const MTPcontacts_Found &result, mtpRequestId requestId);
base::Timer _timer;
QString _query;
mtpRequestId _requestId = 0;
std::map<QString, MTPcontacts_Found> _cache;
std::map<mtpRequestId, QString> _queries;
};
class ChatsListBoxController : public PeerListController, protected base::Subscriber {
public:
ChatsListBoxController(std::unique_ptr<PeerListSearchController> searchController = std::make_unique<PeerListGlobalSearchController>());
void prepare() override final;
std::unique_ptr<PeerListBox::Row> createGlobalRow(PeerData *peer) override final;
std::unique_ptr<PeerListRow> createSearchRow(gsl::not_null<PeerData*> peer) override final;
protected:
class Row : public PeerListBox::Row {
class Row : public PeerListRow {
public:
Row(History *history) : PeerListBox::Row(history->peer), _history(history) {
Row(gsl::not_null<History*> history) : PeerListRow(history->peer), _history(history) {
}
History *history() const {
gsl::not_null<History*> history() const {
return _history;
}
private:
History *_history = nullptr;
gsl::not_null<History*> _history;
};
virtual std::unique_ptr<Row> createRow(History *history) = 0;
virtual std::unique_ptr<Row> createRow(gsl::not_null<History*> history) = 0;
virtual void prepareViewHook() = 0;
virtual void updateRowHook(Row *row) {
}

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/photo_crop_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "messenger.h"
#include "mainwidget.h"
#include "storage/file_upload.h"
@@ -50,8 +50,8 @@ void PhotoCropBox::init(const QImage &img, PeerData *peer) {
}
void PhotoCropBox::prepare() {
addButton(lang(lng_settings_save), [this] { sendPhoto(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_settings_save), [this] { sendPhoto(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
if (peerToBareInt(_peerId)) {
connect(this, SIGNAL(ready(const QImage&)), this, SLOT(onReady(const QImage&)));
}

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/rate_call_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "styles/style_boxes.h"
#include "styles/style_calls.h"
#include "boxes/confirm_box.h"
@@ -42,10 +42,8 @@ RateCallBox::RateCallBox(QWidget*, uint64 callId, uint64 callAccessHash)
}
void RateCallBox::prepare() {
auto titleWidth = st::boxWideWidth - 2 * st::boxTitlePosition.x();
auto titleText = st::boxTitleFont->elided(lang(lng_call_rate_label), titleWidth);
setTitle(titleText);
addButton(lang(lng_cancel), [this] { closeBox(); });
setTitle(langFactory(lng_call_rate_label));
addButton(langFactory(lng_cancel), [this] { closeBox(); });
for (auto i = 0; i < kMaxRating; ++i) {
_stars.push_back(object_ptr<Ui::IconButton>(this, st::callRatingStar));
@@ -75,8 +73,8 @@ void RateCallBox::ratingChanged(int value) {
Expects(value > 0 && value <= kMaxRating);
if (!_rating) {
clearButtons();
addButton(lang(lng_send_button), [this] { onSend(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_send_button), [this] { onSend(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
}
_rating = value;
@@ -86,7 +84,7 @@ void RateCallBox::ratingChanged(int value) {
}
if (value < kMaxRating) {
if (!_comment) {
_comment.create(this, st::callRatingComment, lang(lng_call_rate_comment));
_comment.create(this, st::callRatingComment, langFactory(lng_call_rate_comment));
_comment->show();
_comment->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
_comment->setMaxLength(MaxPhotoCaption);

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/report_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "styles/style_boxes.h"
#include "styles/style_profile.h"
#include "boxes/confirm_box.h"
@@ -38,10 +38,10 @@ ReportBox::ReportBox(QWidget*, PeerData *peer) : _peer(peer)
}
void ReportBox::prepare() {
setTitle(lang(_peer->isUser() ? lng_report_bot_title : (_peer->isMegagroup() ? lng_report_group_title : lng_report_title)));
setTitle(langFactory(_peer->isUser() ? lng_report_bot_title : (_peer->isMegagroup() ? lng_report_group_title : lng_report_title)));
addButton(lang(lng_report_button), [this] { onReport(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_report_button), [this] { onReport(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
_reasonGroup->setChangedCallback([this](Reason value) { reasonChanged(value); });
@@ -51,7 +51,7 @@ void ReportBox::prepare() {
void ReportBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_reasonSpam->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxOptionListPadding.top());
_reasonSpam->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxOptionListPadding.top() + _reasonSpam->getMargins().top());
_reasonViolence->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonSpam->bottomNoMargins() + st::boxOptionListSkip);
_reasonPornography->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonViolence->bottomNoMargins() + st::boxOptionListSkip);
_reasonOther->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonPornography->bottomNoMargins() + st::boxOptionListSkip);
@@ -64,7 +64,7 @@ void ReportBox::resizeEvent(QResizeEvent *e) {
void ReportBox::reasonChanged(Reason reason) {
if (reason == Reason::Other) {
if (!_reasonOtherText) {
_reasonOtherText.create(this, st::profileReportReasonOther, lang(lng_report_reason_description));
_reasonOtherText.create(this, st::profileReportReasonOther, langFactory(lng_report_reason_description));
_reasonOtherText->show();
_reasonOtherText->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
_reasonOtherText->setMaxLength(MaxPhotoCaption);
@@ -131,7 +131,7 @@ bool ReportBox::reportFail(const RPCError &error) {
}
void ReportBox::updateMaxHeight() {
auto newHeight = st::boxOptionListPadding.top() + 4 * _reasonSpam->heightNoMargins() + 3 * st::boxOptionListSkip + st::boxOptionListPadding.bottom();
auto newHeight = st::boxOptionListPadding.top() + _reasonSpam->getMargins().top() + 4 * _reasonSpam->heightNoMargins() + 3 * st::boxOptionListSkip + _reasonSpam->getMargins().bottom() + st::boxOptionListPadding.bottom();
if (_reasonOtherText) {
newHeight += st::newGroupDescriptionPadding.top() + _reasonOtherText->height() + st::newGroupDescriptionPadding.bottom();
}

View File

@@ -20,20 +20,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/self_destruction_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "styles/style_boxes.h"
void SelfDestructionBox::prepare() {
setTitle(lang(lng_self_destruct_title));
setTitle(langFactory(lng_self_destruct_title));
_ttlValues = { 30, 90, 180, 365 };
auto fake = object_ptr<Ui::FlatLabel>(this, lang(lng_self_destruct_description), Ui::FlatLabel::InitType::Simple, st::boxLabel);
auto boxHeight = st::boxOptionListPadding.top()
+ fake->height() + st::boxMediumSkip
+ _ttlValues.size() * (st::langsButton.height + st::boxOptionListSkip) - st::boxOptionListSkip
+ _ttlValues.size() * (st::defaultRadio.diameter + st::boxOptionListSkip) - st::boxOptionListSkip
+ st::boxOptionListPadding.bottom() + st::boxPadding.bottom();
fake.destroy();
@@ -42,7 +42,7 @@ void SelfDestructionBox::prepare() {
auto loading = object_ptr<Ui::FlatLabel>(this, lang(lng_contacts_loading), Ui::FlatLabel::InitType::Simple, st::membersAbout);
loading->moveToLeft((st::boxWidth - loading->width()) / 2, boxHeight / 3);
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
request(MTPaccount_GetAccountTTL()).done([this, loading = std::move(loading)](const MTPAccountDaysTTL &result) mutable {
Expects(result.type() == mtpc_accountDaysTTL);
@@ -72,10 +72,10 @@ void SelfDestructionBox::prepare() {
showChildren();
clearButtons();
addButton(lang(lng_settings_save), [this, group] {
addButton(langFactory(lng_settings_save), [this, group] {
MTP::send(MTPaccount_SetAccountTTL(MTP_accountDaysTTL(MTP_int(group->value()))));
closeBox();
});
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
}).send();
}

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/send_files_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "mainwidget.h"
#include "history/history_media_types.h"
@@ -46,7 +46,7 @@ bool ValidatePhotoDimensions(int width, int height) {
SendFilesBox::SendFilesBox(QWidget*, QImage image, CompressConfirm compressed)
: _image(image)
, _compressConfirm(compressed)
, _caption(this, st::confirmCaptionArea, lang(lng_photo_caption)) {
, _caption(this, st::confirmCaptionArea, langFactory(lng_photo_caption)) {
_files.push_back(QString());
prepareSingleFileLayout();
}
@@ -54,7 +54,7 @@ SendFilesBox::SendFilesBox(QWidget*, QImage image, CompressConfirm compressed)
SendFilesBox::SendFilesBox(QWidget*, const QStringList &files, CompressConfirm compressed)
: _files(files)
, _compressConfirm(compressed)
, _caption(this, st::confirmCaptionArea, lang(_files.size() > 1 ? lng_photos_comment : lng_photo_caption)) {
, _caption(this, st::confirmCaptionArea, langFactory(_files.size() > 1 ? lng_photos_comment : lng_photo_caption)) {
if (_files.size() == 1) {
prepareSingleFileLayout();
}
@@ -237,14 +237,14 @@ void SendFilesBox::prepare() {
updateTitleText();
}
_send = addButton(lang(lng_send_button), [this] { onSend(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
_send = addButton(langFactory(lng_send_button), [this] { onSend(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
if (_compressConfirm != CompressConfirm::None) {
auto compressed = (_compressConfirm == CompressConfirm::Auto) ? cCompressPastedImage() : (_compressConfirm == CompressConfirm::Yes);
auto text = lng_send_images_compress(lt_count, _files.size());
_compressed.create(this, text, compressed, st::defaultBoxCheckbox);
connect(_compressed, SIGNAL(changed()), this, SLOT(onCompressedChange()));
subscribe(_compressed->checkedChanged, [this](bool checked) { onCompressedChange(); });
}
if (_caption) {
_caption->setMaxLength(MaxPhotoCaption);
@@ -258,13 +258,13 @@ void SendFilesBox::prepare() {
updateBoxSize();
}
QString SendFilesBox::getSendButtonText() const {
base::lambda<QString()> SendFilesBox::getSendButtonText() const {
if (!_contactPhone.isEmpty()) {
return lang(lng_send_button);
return langFactory(lng_send_button);
} else if (_compressed && _compressed->checked()) {
return lng_send_photos(lt_count, _files.size());
return [count = _files.size()] { return lng_send_photos(lt_count, count); };
}
return lng_send_files(lt_count, _files.size());
return [count = _files.size()] { return lng_send_files(lt_count, count); };
}
void SendFilesBox::onCompressedChange() {
@@ -438,7 +438,7 @@ void SendFilesBox::onSend(bool ctrlShiftEnter) {
_confirmed = true;
if (_confirmedCallback) {
auto compressed = _compressed ? _compressed->checked() : false;
auto caption = _caption ? prepareText(_caption->getLastText(), true) : QString();
auto caption = _caption ? TextUtilities::PrepareForSending(_caption->getLastText(), TextUtilities::PrepareTextOption::CheckLinks) : QString();
_confirmedCallback(_files, _animated ? QImage() : _image, std::move(_information), compressed, caption, ctrlShiftEnter);
}
closeBox();
@@ -564,7 +564,7 @@ EditCaptionBox::EditCaptionBox(QWidget*, HistoryMedia *media, FullMsgId msgId) :
}
t_assert(_animated || _photo || _doc);
_field.create(this, st::confirmCaptionArea, lang(lng_photo_caption), caption);
_field.create(this, st::confirmCaptionArea, langFactory(lng_photo_caption), caption);
_field->setMaxLength(MaxPhotoCaption);
_field->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
}
@@ -607,8 +607,8 @@ void EditCaptionBox::clipCallback(Media::Clip::Notification notification) {
}
void EditCaptionBox::prepare() {
addButton(lang(lng_settings_save), [this] { onSave(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_settings_save), [this] { onSave(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
updateBoxSize();
connect(_field, SIGNAL(submitted(bool)), this, SLOT(onSave(bool)));
@@ -766,7 +766,7 @@ void EditCaptionBox::onSave(bool ctrlShiftEnter) {
if (!sentEntities.v.isEmpty()) {
flags |= MTPmessages_EditMessage::Flag::f_entities;
}
auto text = prepareText(_field->getLastText(), true);
auto text = TextUtilities::PrepareForSending(_field->getLastText(), TextUtilities::PrepareTextOption::CheckLinks);
_saveRequestId = MTP::send(MTPmessages_EditMessage(MTP_flags(flags), item->history()->peer->input, MTP_int(item->id), MTP_string(text), MTPnullMarkup, sentEntities), rpcDone(&EditCaptionBox::saveDone), rpcFail(&EditCaptionBox::saveFail));
}

View File

@@ -72,7 +72,7 @@ private:
void updateTitleText();
void updateBoxSize();
void updateControlsGeometry();
QString getSendButtonText() const;
base::lambda<QString()> getSendButtonText() const;
QString _titleText;
QStringList _files;

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/sessions_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "mainwidget.h"
#include "mainwindow.h"
@@ -35,9 +35,9 @@ SessionsBox::SessionsBox(QWidget*)
}
void SessionsBox::prepare() {
setTitle(lang(lng_sessions_other_header));
setTitle(langFactory(lng_sessions_other_header));
addButton(lang(lng_close), [this] { closeBox(); });
addButton(langFactory(lng_close), [this] { closeBox(); });
setDimensions(st::boxWideWidth, st::sessionsHeight);
@@ -117,8 +117,8 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
if (appVer == QString::number(appVer.toInt())) {
int32 ver = appVer.toInt();
appVer = QString("%1.%2").arg(ver / 1000000).arg((ver % 1000000) / 1000) + ((ver % 1000) ? ('.' + QString::number(ver % 1000)) : QString());
} else {
appVer = QString();
//} else {
// appVer = QString();
}
} else {
appName = qs(d.vapp_name);// +qsl(" for ") + qs(d.vplatform);

View File

@@ -24,7 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_boxes.h"
#include "styles/style_history.h"
#include "observer_peer.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "mainwindow.h"
#include "mainwidget.h"
#include "base/qthelp_url.h"
@@ -34,6 +34,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/toast/toast.h"
#include "ui/widgets/multi_select.h"
#include "history/history_media_types.h"
#include "history/history_message.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "window/themes/window_theme.h"
@@ -45,7 +46,7 @@ ShareBox::ShareBox(QWidget*, CopyCallback &&copyCallback, SubmitCallback &&submi
: _copyCallback(std::move(copyCallback))
, _submitCallback(std::move(submitCallback))
, _filterCallback(std::move(filterCallback))
, _select(this, st::contactsMultiSelect, lang(lng_participant_filter))
, _select(this, st::contactsMultiSelect, langFactory(lng_participant_filter))
, _searchTimer(this) {
}
@@ -53,7 +54,7 @@ void ShareBox::prepare() {
_select->resizeToWidth(st::boxWideWidth);
myEnsureResized(_select);
setTitle(lang(lng_share_title));
setTitle(langFactory(lng_share_title));
_inner = setInnerWidget(object_ptr<Inner>(this, std::move(_filterCallback)), getTopScrollSkip());
connect(_inner, SIGNAL(mustScrollTo(int,int)), this, SLOT(onMustScrollTo(int,int)));
@@ -207,11 +208,11 @@ void ShareBox::updateButtons() {
void ShareBox::createButtons() {
clearButtons();
if (_hasSelected) {
addButton(lang(lng_share_confirm), [this] { onSubmit(); });
} else {
addButton(lang(lng_share_copy_link), [this] { onCopyLink(); });
addButton(langFactory(lng_share_confirm), [this] { onSubmit(); });
} else if (_copyCallback) {
addButton(langFactory(lng_share_copy_link), [this] { onCopyLink(); });
}
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
}
void ShareBox::onFilterUpdate(const QString &query) {
@@ -674,21 +675,9 @@ bool ShareBox::Inner::hasSelected() const {
void ShareBox::Inner::updateFilter(QString filter) {
_lastQuery = filter.toLower().trimmed();
filter = textSearchKey(filter);
QStringList f;
if (!filter.isEmpty()) {
QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts);
int l = filterList.size();
f.reserve(l);
for (int i = 0; i < l; ++i) {
QString filterName = filterList[i].trimmed();
if (filterName.isEmpty()) continue;
f.push_back(filterName);
}
filter = f.join(' ');
}
auto words = TextUtilities::PrepareSearchWords(_lastQuery);
filter = words.isEmpty() ? QString() : words.join(' ');
if (_filter != filter) {
_filter = filter;
@@ -701,10 +690,10 @@ void ShareBox::Inner::updateFilter(QString filter) {
if (_filter.isEmpty()) {
refresh();
} else {
QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi;
QStringList::const_iterator fb = words.cbegin(), fe = words.cend(), fi;
_filtered.clear();
if (!f.isEmpty()) {
if (!words.isEmpty()) {
const Dialogs::List *toFilter = nullptr;
if (!_chatsIndexed->isEmpty()) {
for (fi = fb; fi != fe; ++fi) {
@@ -809,7 +798,7 @@ QVector<PeerData*> ShareBox::Inner::selected() const {
return result;
}
QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId) {
QString AppendShareGameScoreUrl(const QString &url, const FullMsgId &fullId) {
auto shareHashData = QByteArray(0x10, Qt::Uninitialized);
auto shareHashDataInts = reinterpret_cast<int32*>(shareHashData.data());
auto channel = fullId.channel ? App::channelLoaded(fullId.channel) : static_cast<ChannelData*>(nullptr);
@@ -852,79 +841,7 @@ QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId) {
return url + shareComponent;
}
namespace {
void shareGameScoreFromItem(HistoryItem *item) {
struct ShareGameScoreData {
ShareGameScoreData(const FullMsgId &msgId) : msgId(msgId) {
}
FullMsgId msgId;
OrderedSet<mtpRequestId> requests;
};
auto data = MakeShared<ShareGameScoreData>(item->fullId());
auto copyCallback = [data]() {
if (auto main = App::main()) {
if (auto item = App::histItemById(data->msgId)) {
if (auto bot = item->getMessageBot()) {
if (auto media = item->getMedia()) {
if (media->type() == MediaTypeGame) {
auto shortName = static_cast<HistoryGame*>(media)->game()->shortName;
QApplication::clipboard()->setText(Messenger::Instance().createInternalLinkFull(bot->username + qsl("?game=") + shortName));
Ui::Toast::Show(lang(lng_share_game_link_copied));
}
}
}
}
}
};
auto submitCallback = [data](const QVector<PeerData*> &result) {
if (!data->requests.empty()) {
return; // Share clicked already.
}
auto doneCallback = [data](const MTPUpdates &updates, mtpRequestId requestId) {
if (auto main = App::main()) {
main->sentUpdatesReceived(updates);
}
data->requests.remove(requestId);
if (data->requests.empty()) {
Ui::Toast::Show(lang(lng_share_done));
Ui::hideLayer();
}
};
auto sendFlags = MTPmessages_ForwardMessages::Flag::f_with_my_score;
MTPVector<MTPint> msgIds = MTP_vector<MTPint>(1, MTP_int(data->msgId.msg));
if (auto main = App::main()) {
if (auto item = App::histItemById(data->msgId)) {
for_const (auto peer, result) {
MTPVector<MTPlong> random = MTP_vector<MTPlong>(1, rand_value<MTPlong>());
auto request = MTPmessages_ForwardMessages(MTP_flags(sendFlags), item->history()->peer->input, msgIds, random, peer->input);
auto callback = doneCallback;
auto requestId = MTP::send(request, rpcDone(std::move(callback)));
data->requests.insert(requestId);
}
}
}
};
auto filterCallback = [](PeerData *peer) {
if (peer->canWrite()) {
if (auto channel = peer->asChannel()) {
return !channel->isBroadcast();
}
return true;
}
return false;
};
Ui::show(Box<ShareBox>(std::move(copyCallback), std::move(submitCallback), std::move(filterCallback)));
}
} // namespace
void shareGameScoreByHash(const QString &hash) {
void ShareGameScoreByHash(const QString &hash) {
auto key128Size = 0x10;
auto hashEncrypted = QByteArray::fromBase64(hash.toLatin1(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
@@ -975,12 +892,12 @@ void shareGameScoreByHash(const QString &hash) {
}
if (auto item = App::histItemById(channelId, msgId)) {
shareGameScoreFromItem(item);
FastShareMessage(item);
} else if (App::api()) {
auto resolveMessageAndShareScore = [msgId](ChannelData *channel) {
App::api()->requestMessageData(channel, msgId, [](ChannelData *channel, MsgId msgId) {
if (auto item = App::histItemById(channel, msgId)) {
shareGameScoreFromItem(item);
FastShareMessage(item);
} else {
Ui::show(Box<InformBox>(lang(lng_edit_deleted)));
}

View File

@@ -37,8 +37,8 @@ namespace Ui {
class MultiSelect;
} // namespace Ui
QString appendShareGameScoreUrl(const QString &url, const FullMsgId &fullId);
void shareGameScoreByHash(const QString &hash);
QString AppendShareGameScoreUrl(const QString &url, const FullMsgId &fullId);
void ShareGameScoreByHash(const QString &hash);
class ShareBox : public BoxContent, public RPCSender {
Q_OBJECT

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/sticker_set_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "chat_helpers/stickers.h"
@@ -46,7 +46,7 @@ StickerSetBox::StickerSetBox(QWidget*, const MTPInputStickerSet &set)
}
void StickerSetBox::prepare() {
setTitle(lang(lng_contacts_loading));
setTitle(langFactory(lng_contacts_loading));
_inner = setInnerWidget(object_ptr<Inner>(this, _set), st::stickersScroll);
connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated()));
@@ -89,16 +89,16 @@ void StickerSetBox::updateButtons() {
clearButtons();
if (_inner->loaded()) {
if (_inner->notInstalled()) {
addButton(lang(lng_stickers_add_pack), [this] { onAddStickers(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_stickers_add_pack), [this] { onAddStickers(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
} else if (_inner->official()) {
addButton(lang(lng_about_done), [this] { closeBox(); });
addButton(langFactory(lng_about_done), [this] { closeBox(); });
} else {
addButton(lang(lng_stickers_share_pack), [this] { onShareStickers(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_stickers_share_pack), [this] { onShareStickers(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
}
} else {
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
}
update();
}
@@ -427,19 +427,18 @@ bool StickerSetBox::Inner::official() const {
return _loaded && _setShortName.isEmpty();
}
TextWithEntities StickerSetBox::Inner::title() const {
auto text = _setTitle;
auto entities = EntitiesInText();
base::lambda<TextWithEntities()> StickerSetBox::Inner::title() const {
auto text = TextWithEntities { _setTitle };
if (_loaded) {
if (_pack.isEmpty()) {
text = lang(lng_attach_failed);
return [] { return TextWithEntities { lang(lng_attach_failed), EntitiesInText() }; };
} else {
textParseEntities(text, TextParseMentions, &entities);
TextUtilities::ParseEntities(text, TextParseMentions);
}
} else {
text = lang(lng_contacts_loading);
return [] { return TextWithEntities { lang(lng_contacts_loading), EntitiesInText() }; };
}
return { text, entities };
return [text] { return text; };
}
QString StickerSetBox::Inner::shortName() const {

View File

@@ -71,7 +71,7 @@ public:
bool loaded() const;
int32 notInstalled() const;
bool official() const;
TextWithEntities title() const;
base::lambda<TextWithEntities()> title() const;
QString shortName() const;
void setVisibleTopBottom(int visibleTop, int visibleBottom) override;

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "stickers_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "chat_helpers/stickers.h"
#include "boxes/confirm_box.h"
@@ -230,7 +230,7 @@ void StickersBox::prepare() {
} else if (_section == Section::Archived) {
requestArchivedSets();
} else if (_section == Section::ArchivedPart) {
setTitle(lang(lng_stickers_archived));
setTitle(langFactory(lng_stickers_archived));
}
if (Global::ArchivedStickerSetsOrder().isEmpty()) {
preloadArchivedSets();
@@ -252,7 +252,7 @@ void StickersBox::prepare() {
_archived.widget()->setInstallSetCallback([this](uint64 setId) { installSet(setId); });
_archived.widget()->setLoadMoreCallback([this] { loadMoreArchived(); });
addButton(lang(lng_about_done), [this] { closeBox(); });
addButton(langFactory(lng_about_done), [this] { closeBox(); });
if (_section == Section::Installed) {
_tab = &_installed;
@@ -713,9 +713,11 @@ void StickersBox::Inner::paintRow(Painter &p, int index, TimeMs ms) {
}
}
auto statusText = (s->count > 0) ? lng_stickers_count(lt_count, s->count) : lang(lng_contacts_loading);
p.setFont(st::contactsStatusFont);
p.setPen(st::contactsStatusFg);
p.drawTextLeft(statusx, statusy, width(), lng_stickers_count(lt_count, s->count));
p.drawTextLeft(statusx, statusy, width(), statusText);
p.setOpacity(1);
if (xadd || yadd) p.translate(-xadd, -yadd);

View File

@@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "boxes/username_box.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "application.h"
#include "mainwidget.h"
#include "mainwindow.h"
@@ -31,7 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "messenger.h"
UsernameBox::UsernameBox(QWidget*)
: _username(this, st::defaultInputField, qsl("@username"), App::self()->username, false)
: _username(this, st::defaultInputField, [] { return qsl("@username"); }, App::self()->username, false)
, _link(this, QString(), st::boxLinkButton)
, _about(st::boxWidth - st::usernamePadding.left())
, _checkTimer(this) {
@@ -40,10 +40,10 @@ UsernameBox::UsernameBox(QWidget*)
void UsernameBox::prepare() {
_goodText = App::self()->username.isEmpty() ? QString() : lang(lng_username_available);
setTitle(lang(lng_username_title));
setTitle(langFactory(lng_username_title));
addButton(lang(lng_settings_save), [this] { onSave(); });
addButton(lang(lng_cancel), [this] { closeBox(); });
addButton(langFactory(lng_settings_save), [this] { onSave(); });
addButton(langFactory(lng_cancel), [this] { closeBox(); });
connect(_username, SIGNAL(changed()), this, SLOT(onChanged()));
connect(_username, SIGNAL(submitted(bool)), this, SLOT(onSave()));
@@ -175,7 +175,7 @@ bool UsernameBox::onUpdateFail(const RPCError &error) {
_saveRequestId = 0;
QString err(error.type());
if (err == qstr("USERNAME_NOT_MODIFIED") || _sentUsername == App::self()->username) {
App::self()->setName(textOneLine(App::self()->firstName), textOneLine(App::self()->lastName), textOneLine(App::self()->nameOrPhone), textOneLine(_sentUsername));
App::self()->setName(TextUtilities::SingleLine(App::self()->firstName), TextUtilities::SingleLine(App::self()->lastName), TextUtilities::SingleLine(App::self()->nameOrPhone), TextUtilities::SingleLine(_sentUsername));
closeBox();
return true;
} else if (err == qstr("USERNAME_INVALID")) {

View File

@@ -22,7 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_calls.h"
#include "styles/style_boxes.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "observer_peer.h"
#include "ui/effects/ripple_animation.h"
#include "calls/calls_instance.h"
@@ -36,7 +36,7 @@ constexpr auto kPerPageCount = 100;
} // namespace
class BoxController::Row : public PeerListBox::Row {
class BoxController::Row : public PeerListRow {
public:
Row(HistoryItem *item);
@@ -77,12 +77,10 @@ public:
return _items.front()->id;
}
protected:
void paintStatusText(Painter &p, int x, int y, int outerWidth, bool selected) override;
void paintStatusText(Painter &p, int x, int y, int availableWidth, int outerWidth, bool selected) override;
void addActionRipple(QPoint point, base::lambda<void()> updateCallback) override;
void stopLastActionRipple() override;
private:
bool needsVerifiedIcon() const override {
return false;
}
@@ -94,6 +92,7 @@ private:
}
void paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) override;
private:
void refreshStatus();
static Type ComputeType(HistoryItem *item);
@@ -105,14 +104,14 @@ private:
};
BoxController::Row::Row(HistoryItem *item) : PeerListBox::Row(item->history()->peer, item->id)
BoxController::Row::Row(HistoryItem *item) : PeerListRow(item->history()->peer, item->id)
, _items(1, item)
, _date(item->date.date())
, _type(ComputeType(item)) {
refreshStatus();
}
void BoxController::Row::paintStatusText(Painter &p, int x, int y, int outerWidth, bool selected) {
void BoxController::Row::paintStatusText(Painter &p, int x, int y, int availableWidth, int outerWidth, bool selected) {
auto icon = ([this] {
switch (_type) {
case Type::In: return &st::callArrowIn;
@@ -122,9 +121,11 @@ void BoxController::Row::paintStatusText(Painter &p, int x, int y, int outerWidt
Unexpected("_type in Calls::BoxController::Row::paintStatusText().");
})();
icon->paint(p, x + st::callArrowPosition.x(), y + st::callArrowPosition.y(), outerWidth);
x += + st::callArrowPosition.x() + icon->width() + st::callArrowSkip;
auto shift = st::callArrowPosition.x() + icon->width() + st::callArrowSkip;
x += shift;
availableWidth -= shift;
PeerListBox::Row::paintStatusText(p, x, y, outerWidth, selected);
PeerListRow::paintStatusText(p, x, y, availableWidth, outerWidth, selected);
}
void BoxController::Row::paintAction(Painter &p, TimeMs ms, int x, int y, int outerWidth, bool actionSelected) {
@@ -188,12 +189,12 @@ void BoxController::prepare() {
if (auto row = rowForItem(item)) {
row->itemRemoved(item);
if (!row->hasItems()) {
view()->removeRow(row);
if (!view()->fullRowsCount()) {
delegate()->peerListRemoveRow(row);
if (!delegate()->peerListFullRowsCount()) {
refreshAbout();
}
}
view()->refreshRows();
delegate()->peerListRefreshRows();
}
});
subscribe(Current().newServiceMessage(), [this](const FullMsgId &msgId) {
@@ -202,20 +203,19 @@ void BoxController::prepare() {
}
});
view()->setTitle(lang(lng_call_box_title));
view()->addButton(lang(lng_close), [this] { view()->closeBox(); });
view()->setAboutText(lang(lng_contacts_loading));
view()->refreshRows();
delegate()->peerListSetTitle(langFactory(lng_call_box_title));
setDescriptionText(lang(lng_contacts_loading));
delegate()->peerListRefreshRows();
preloadRows();
loadMoreRows();
}
void BoxController::preloadRows() {
void BoxController::loadMoreRows() {
if (_loadRequestId || _allLoaded) {
return;
}
_loadRequestId = request(MTPmessages_Search(MTP_flags(0), MTP_inputPeerEmpty(), MTP_string(QString()), MTP_inputMessagesFilterPhoneCalls(MTP_flags(0)), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(_offsetId), MTP_int(_offsetId ? kFirstPageCount : kPerPageCount))).done([this](const MTPmessages_Messages &result) {
_loadRequestId = request(MTPmessages_Search(MTP_flags(0), MTP_inputPeerEmpty(), MTP_string(QString()), MTP_inputUserEmpty(), MTP_inputMessagesFilterPhoneCalls(MTP_flags(0)), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(_offsetId), MTP_int(_offsetId ? kFirstPageCount : kPerPageCount))).done([this](const MTPmessages_Messages &result) {
_loadRequestId = 0;
auto handleResult = [this](auto &data) {
@@ -240,16 +240,16 @@ void BoxController::preloadRows() {
}
void BoxController::refreshAbout() {
view()->setAboutText(view()->fullRowsCount() ? QString() : lang(lng_call_box_about));
setDescriptionText(delegate()->peerListFullRowsCount() ? QString() : lang(lng_call_box_about));
}
void BoxController::rowClicked(PeerListBox::Row *row) {
auto itemsRow = static_cast<Row*>(row);
void BoxController::rowClicked(gsl::not_null<PeerListRow*> row) {
auto itemsRow = static_cast<Row*>(row.get());
auto itemId = itemsRow->maxItemId();
Ui::showPeerHistoryAsync(row->peer()->id, itemId);
}
void BoxController::rowActionClicked(PeerListBox::Row *row) {
void BoxController::rowActionClicked(gsl::not_null<PeerListRow*> row) {
auto user = row->peer()->asUser();
t_assert(user != nullptr);
@@ -274,7 +274,7 @@ void BoxController::receivedCalls(const QVector<MTPMessage> &result) {
}
refreshAbout();
view()->refreshRows();
delegate()->peerListRefreshRows();
}
bool BoxController::insertRow(HistoryItem *item, InsertWay way) {
@@ -284,24 +284,22 @@ bool BoxController::insertRow(HistoryItem *item, InsertWay way) {
return false;
}
}
(way == InsertWay::Append) ? view()->appendRow(createRow(item)) : view()->prependRow(createRow(item));
view()->reorderRows([](auto &&begin, auto &&end) {
std::sort(begin, end, [](auto &a, auto &b) {
return static_cast<Row&>(*a).maxItemId() > static_cast<Row&>(*a).maxItemId();
});
(way == InsertWay::Append) ? delegate()->peerListAppendRow(createRow(item)) : delegate()->peerListPrependRow(createRow(item));
delegate()->peerListSortRows([](PeerListRow &a, PeerListRow &b) {
return static_cast<Row&>(a).maxItemId() > static_cast<Row&>(b).maxItemId();
});
return true;
}
BoxController::Row *BoxController::rowForItem(HistoryItem *item) {
auto v = view();
if (auto fullRowsCount = v->fullRowsCount()) {
auto v = delegate();
if (auto fullRowsCount = v->peerListFullRowsCount()) {
auto itemId = item->id;
auto lastRow = static_cast<Row*>(v->rowAt(fullRowsCount - 1));
auto lastRow = static_cast<Row*>(v->peerListRowAt(fullRowsCount - 1).get());
if (itemId < lastRow->minItemId()) {
return lastRow;
}
auto firstRow = static_cast<Row*>(v->rowAt(0));
auto firstRow = static_cast<Row*>(v->peerListRowAt(0).get());
if (itemId > firstRow->maxItemId()) {
return firstRow;
}
@@ -313,18 +311,18 @@ BoxController::Row *BoxController::rowForItem(HistoryItem *item) {
auto right = fullRowsCount;
while (left + 1 < right) {
auto middle = (right + left) / 2;
auto middleRow = static_cast<Row*>(v->rowAt(middle));
auto middleRow = static_cast<Row*>(v->peerListRowAt(middle).get());
if (middleRow->maxItemId() >= itemId) {
left = middle;
} else {
right = middle;
}
}
auto result = static_cast<Row*>(v->rowAt(left));
auto result = static_cast<Row*>(v->peerListRowAt(left).get());
// Check for rowAt(left)->minItemId > itemId > rowAt(left + 1)->maxItemId.
// In that case we sometimes need to return rowAt(left + 1), not rowAt(left).
if (result->minItemId() > itemId && left + 1 < fullRowsCount) {
auto possibleResult = static_cast<Row*>(v->rowAt(left + 1));
auto possibleResult = static_cast<Row*>(v->peerListRowAt(left + 1).get());
t_assert(possibleResult->maxItemId() < itemId);
if (possibleResult->canAddItem(item)) {
return possibleResult;
@@ -335,7 +333,7 @@ BoxController::Row *BoxController::rowForItem(HistoryItem *item) {
return nullptr;
}
std::unique_ptr<PeerListBox::Row> BoxController::createRow(HistoryItem *item) const {
std::unique_ptr<PeerListRow> BoxController::createRow(HistoryItem *item) const {
auto row = std::make_unique<Row>(item);
return std::move(row);
}

View File

@@ -24,12 +24,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Calls {
class BoxController : public PeerListBox::Controller, private base::Subscriber, private MTP::Sender {
class BoxController : public PeerListController, private base::Subscriber, private MTP::Sender {
public:
void prepare() override;
void rowClicked(PeerListBox::Row *row) override;
void rowActionClicked(PeerListBox::Row *row) override;
void preloadRows() override;
void rowClicked(gsl::not_null<PeerListRow*> row) override;
void rowActionClicked(gsl::not_null<PeerListRow*> row) override;
void loadMoreRows() override;
private:
void receivedCalls(const QVector<MTPMessage> &result);
@@ -43,7 +43,7 @@ private:
Prepend,
};
bool insertRow(HistoryItem *item, InsertWay way);
std::unique_ptr<PeerListBox::Row> createRow(HistoryItem *item) const;
std::unique_ptr<PeerListRow> createRow(HistoryItem *item) const;
MsgId _offsetId = 0;
mtpRequestId _loadRequestId = 0;

View File

@@ -22,7 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "auth_session.h"
#include "mainwidget.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "boxes/confirm_box.h"
#include "boxes/rate_call_box.h"
#include "calls/calls_instance.h"
@@ -231,7 +231,7 @@ void Call::hangup() {
_delegate->callFinished(this);
} else {
auto missed = (_state == State::Ringing || (_state == State::Waiting && _type == Type::Outgoing));
auto declined = (_state == State::WaitingIncoming);
auto declined = isIncomingWaiting();
auto reason = missed ? MTP_phoneCallDiscardReasonMissed() :
declined ? MTP_phoneCallDiscardReasonBusy() : MTP_phoneCallDiscardReasonHangup();
finish(FinishType::Ended, reason);
@@ -597,7 +597,7 @@ void Call::setState(State state) {
break;
case State::Ended:
_delegate->playSound(Delegate::Sound::Ended);
// fallthrough
// [[fallthrough]]
case State::EndedByOtherDevice:
_delegate->callFinished(this);
break;

View File

@@ -24,7 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "messenger.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "boxes/confirm_box.h"
#include "calls/calls_call.h"
#include "calls/calls_panel.h"
@@ -195,7 +195,7 @@ void Instance::refreshServerConfig() {
auto error = QJsonParseError { 0, QJsonParseError::NoError };
auto document = QJsonDocument::fromJson(QByteArray::fromRawData(reinterpret_cast<const char*>(bytes.data()), bytes.size()), &error);
if (error.error != QJsonParseError::NoError) {
LOG(("API Error: Faild to parse call config JSON, error: %1").arg(error.errorString()));
LOG(("API Error: Failed to parse call config JSON, error: %1").arg(error.errorString()));
return;
} else if (!document.isObject()) {
LOG(("API Error: Not an object received in call config JSON."));

View File

@@ -30,7 +30,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/effects/widget_fade_wrap.h"
#include "messenger.h"
#include "mainwindow.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "observer_peer.h"
@@ -473,6 +473,7 @@ void Panel::createUserpicCache(ImagePtr image) {
if (cRetina()) _userPhoto.setDevicePixelRatio(cRetinaFactor());
} else {
auto filled = QImage(QSize(st::callWidth, st::callWidth) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
filled.setDevicePixelRatio(cRetinaFactor());
{
Painter p(&filled);
EmptyUserpic(_user->colorIndex(), _user->name).paintSquare(p, 0, 0, st::callWidth, st::callWidth);

View File

@@ -23,7 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_calls.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "lang.h"
#include "lang/lang_keys.h"
#include "calls/calls_call.h"
#include "calls/calls_instance.h"
#include "styles/style_boxes.h"
@@ -56,8 +56,9 @@ DebugInfoBox::DebugInfoBox(QWidget*, base::weak_unique_ptr<Call> call) : _call(c
}
void DebugInfoBox::prepare() {
setTitle("Call Debug");
addButton(lang(lng_close), [this] { closeBox(); });
setTitle([] { return QString("Call Debug"); });
addButton(langFactory(lng_close), [this] { closeBox(); });
_text = setInnerWidget(object_ptr<Ui::FlatLabel>(this, st::callDebugLabel));
_text->setSelectable(true);
updateText();

View File

@@ -52,7 +52,7 @@ const style::TextStyle &BotKeyboard::Style::textStyle() const {
return st::botKbStyle;
}
void BotKeyboard::Style::repaint(const HistoryItem *item) const {
void BotKeyboard::Style::repaint(gsl::not_null<const HistoryItem*> item) const {
_parent->update();
}
@@ -237,10 +237,10 @@ void BotKeyboard::updateSelected() {
if (!_impl) return;
QPoint p(mapFromGlobal(_lastMousePos));
int x = rtl() ? st::botKbScroll.width : _st->margin;
auto p = mapFromGlobal(_lastMousePos);
auto x = rtl() ? st::botKbScroll.width : _st->margin;
auto link = _impl->getState(p.x() - x, p.y() - _st->margin);
auto link = _impl->getState(p - QPoint(x, _st->margin));
if (ClickHandler::setActive(link, this)) {
Ui::Tooltip::Hide();
setCursor(link ? style::cur_pointer : style::cur_default);

View File

@@ -92,7 +92,7 @@ private:
void startPaint(Painter &p) const override;
const style::TextStyle &textStyle() const override;
void repaint(const HistoryItem *item) const override;
void repaint(gsl::not_null<const HistoryItem*> item) const override;
protected:
void paintButtonBg(Painter &p, const QRect &rect, float64 howMuchOver) const override;

View File

@@ -28,6 +28,11 @@ switchPmButton: RoundButton(defaultBoxButton) {
height: 34px;
textTop: 7px;
}
stickersRestrictedLabel: FlatLabel(defaultFlatLabel) {
width: 320px;
align: align(center);
textFg: noContactsColor;
}
stickersTrendingHeader: 45px;
stickersTrendingSkip: 15px;
@@ -96,8 +101,6 @@ stickersReorderFg: windowSubTextFg;
stickersRowDisabledOpacity: 0.4;
stickersRowDuration: 200;
emojiIconFg: checkboxFg;
emojiIconFgActive: windowBgActive;
stickersSettings: icon {{ "emoji_settings", emojiIconFg }};
stickersTrending: icon {{ "emoji_trending", emojiIconFg }};
stickersTrendingActive: icon {{ "emoji_trending", emojiIconFgActive }};
@@ -219,3 +222,19 @@ gifsSearchCancel: contactsSearchCancel;
gifsSearchCancelPosition: point(1px, 1px);
gifsSearchIcon: boxFieldSearchIcon;
gifsSearchIconPosition: point(6px, 7px);
emojiSuggestionsDropdown: InnerDropdown(defaultInnerDropdown) {
scroll: ScrollArea(defaultSolidScroll) {
deltat: 0px;
deltab: 0px;
round: 1px;
width: 8px;
deltax: 3px;
}
scrollMargin: margins(0px, 5px, 0px, 5px);
scrollPadding: margins(0px, 3px, 0px, 3px);
}
emojiSuggestionsMenu: Menu(defaultMenu) {
itemPadding: margins(48px, 8px, 17px, 7px);
widthMax: 512px;
}

View File

@@ -23,14 +23,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/buttons.h"
#include "styles/style_chat_helpers.h"
#include "ui/widgets/shadow.h"
#include "lang.h"
#include "lang/lang_keys.h"
namespace ChatHelpers {
namespace {
constexpr auto kEmojiPanelPerRow = Ui::Emoji::kPanelPerRow;
constexpr auto kEmojiPanelRowsPerPage = Ui::Emoji::kPanelRowsPerPage;
constexpr auto kSaveRecentEmojiTimeout = 3000;
} // namespace
@@ -536,41 +535,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
}
void EmojiListWidget::selectEmoji(EmojiPtr emoji) {
auto &recent = Ui::Emoji::GetRecent();
auto i = recent.begin(), e = recent.end();
for (; i != e; ++i) {
if (i->first == emoji) {
++i->second;
if (i->second > 0x8000) {
for (auto j = recent.begin(); j != e; ++j) {
if (j->second > 1) {
j->second /= 2;
} else {
j->second = 1;
}
}
}
for (; i != recent.begin(); --i) {
if ((i - 1)->second > i->second) {
break;
}
qSwap(*i, *(i - 1));
}
break;
}
}
if (i == e) {
while (recent.size() >= kEmojiPanelPerRow * kEmojiPanelRowsPerPage) recent.pop_back();
recent.push_back(qMakePair(emoji, 1));
for (i = recent.end() - 1; i != recent.begin(); --i) {
if ((i - 1)->second > i->second) {
break;
}
qSwap(*i, *(i - 1));
}
}
emit saveConfigDelayed(kSaveRecentEmojiTimeout);
Ui::Emoji::AddRecent(emoji);
emit selected(emoji);
}

View File

@@ -0,0 +1,40 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "emoji_suggestions.h"
#include "emoji_suggestions_data.h"
namespace Ui {
namespace Emoji {
inline utf16string QStringToUTF16(const QString &string) {
return utf16string(reinterpret_cast<const utf16char*>(string.constData()), string.size());
}
inline QString QStringFromUTF16(utf16string string) {
return QString::fromRawData(reinterpret_cast<const QChar*>(string.data()), string.size());
}
constexpr auto kSuggestionMaxLength = internal::kReplacementMaxLength;
} // namespace Emoji
} // namespace Ui

View File

@@ -0,0 +1,587 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/emoji_suggestions_helper.h"
#include "ui/effects/ripple_animation.h"
#include "ui/widgets/shadow.h"
#include "platform/platform_specific.h"
#include "styles/style_chat_helpers.h"
#include "ui/widgets/inner_dropdown.h"
namespace Ui {
namespace Emoji {
namespace {
constexpr auto kRowLimit = 5;
} // namespace
class SuggestionsWidget::Row {
public:
Row(gsl::not_null<EmojiPtr> emoji, const QString &label, const QString &replacement);
Row(const Row &other) = delete;
Row &operator=(const Row &other) = delete;
Row(Row &&other) = default;
Row &operator=(Row &&other) = default;
~Row();
gsl::not_null<EmojiPtr> emoji() const {
return _emoji;
}
const QString &label() const {
return _label;
}
const QString &replacement() const {
return _replacement;
}
RippleAnimation *ripple() const {
return _ripple.get();
}
void setRipple(std::unique_ptr<RippleAnimation> ripple) {
_ripple = std::move(ripple);
}
void resetRipple() {
_ripple.reset();
}
private:
gsl::not_null<EmojiPtr> _emoji;
QString _label;
QString _replacement;
std::unique_ptr<RippleAnimation> _ripple;
};
SuggestionsWidget::Row::Row(gsl::not_null<EmojiPtr> emoji, const QString &label, const QString &replacement)
: _emoji(emoji)
, _label(label)
, _replacement(replacement) {
}
SuggestionsWidget::Row::~Row() = default;
SuggestionsWidget::SuggestionsWidget(QWidget *parent, const style::Menu &st) : TWidget(parent)
, _st(&st)
, _rowHeight(_st->itemPadding.top() + _st->itemFont->height + _st->itemPadding.bottom()) {
setMouseTracking(true);
}
void SuggestionsWidget::showWithQuery(const QString &query) {
if (_query == query) {
return;
}
_query = query;
auto rows = getRowsByQuery();
if (rows.empty()) {
toggleAnimated.notify(false, true);
}
clearSelection();
_rows = std::move(rows);
resizeToRows();
update();
if (!_rows.empty()) {
setSelected(0);
}
if (!_rows.empty()) {
toggleAnimated.notify(true, true);
}
}
std::vector<SuggestionsWidget::Row> SuggestionsWidget::getRowsByQuery() const {
auto result = std::vector<Row>();
if (_query.isEmpty()) {
return result;
}
auto suggestions = GetSuggestions(QStringToUTF16(_query));
if (suggestions.empty()) {
return result;
}
auto count = suggestions.size();
auto suggestionsEmoji = std::vector<EmojiPtr>(count, nullptr);
for (auto i = 0; i != count; ++i) {
suggestionsEmoji[i] = Find(QStringFromUTF16(suggestions[i].emoji()));
}
auto recents = 0;
auto &recent = GetRecent();
for (auto &item : recent) {
auto emoji = item.first->original();
if (!emoji) emoji = item.first;
auto it = std::find(suggestionsEmoji.begin(), suggestionsEmoji.end(), emoji);
if (it != suggestionsEmoji.end()) {
auto index = (it - suggestionsEmoji.begin());
if (index >= recents) {
if (index > recents) {
auto recentEmoji = suggestionsEmoji[index];
auto recentSuggestion = suggestions[index];
for (auto i = index; i != recents; --i) {
suggestionsEmoji[i] = suggestionsEmoji[i - 1];
suggestions[i] = suggestions[i - 1];
}
suggestionsEmoji[recents] = recentEmoji;
suggestions[recents] = recentSuggestion;
}
++recents;
}
}
}
result.reserve(kRowLimit);
auto index = 0;
for (auto &item : suggestions) {
if (auto emoji = suggestionsEmoji[index++]) {
if (emoji->hasVariants()) {
auto it = cEmojiVariants().constFind(emoji->nonColoredId());
if (it != cEmojiVariants().cend()) {
emoji = emoji->variant(it.value());
}
}
result.emplace_back(emoji, QStringFromUTF16(item.label()), QStringFromUTF16(item.replacement()));
if (result.size() == kRowLimit) {
break;
}
}
}
return result;
}
void SuggestionsWidget::resizeToRows() {
auto newWidth = 0;
for (auto &row : _rows) {
accumulate_max(newWidth, countWidth(row));
}
newWidth = snap(newWidth, _st->widthMin, _st->widthMax);
auto newHeight = _st->skip + (_rows.size() * _rowHeight) + _st->skip;
resize(newWidth, newHeight);
}
int SuggestionsWidget::countWidth(const Row &row) {
auto textw = _st->itemFont->width(row.label());
return _st->itemPadding.left() + textw + _st->itemPadding.right();
}
void SuggestionsWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
auto ms = getms();
auto clip = e->rect();
auto topskip = QRect(0, 0, width(), _st->skip);
auto bottomskip = QRect(0, height() - _st->skip, width(), _st->skip);
if (clip.intersects(topskip)) p.fillRect(clip.intersected(topskip), _st->itemBg);
if (clip.intersects(bottomskip)) p.fillRect(clip.intersected(bottomskip), _st->itemBg);
auto top = _st->skip;
p.setFont(_st->itemFont);
auto from = floorclamp(clip.top() - top, _rowHeight, 0, _rows.size());
auto to = ceilclamp(clip.top() + clip.height() - top, _rowHeight, 0, _rows.size());
p.translate(0, top + from * _rowHeight);
for (auto i = from; i != to; ++i) {
auto &row = _rows[i];
auto selected = (i == _selected || i == _pressed);
p.fillRect(0, 0, width(), _rowHeight, selected ? _st->itemBgOver : _st->itemBg);
if (auto ripple = row.ripple()) {
ripple->paint(p, 0, 0, width(), ms);
if (ripple->empty()) {
row.resetRipple();
}
}
auto emoji = row.emoji();
auto esize = Ui::Emoji::Size(Ui::Emoji::Index() + 1);
p.drawPixmapLeft((_st->itemPadding.left() - (esize / cIntRetinaFactor())) / 2, (_rowHeight - (esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(emoji->x() * esize, emoji->y() * esize, esize, esize));
p.setPen(selected ? _st->itemFgOver : _st->itemFg);
p.drawTextLeft(_st->itemPadding.left(), _st->itemPadding.top(), width(), row.label());
p.translate(0, _rowHeight);
}
}
void SuggestionsWidget::keyPressEvent(QKeyEvent *e) {
handleKeyEvent(e->key());
}
void SuggestionsWidget::handleKeyEvent(int key) {
if (key == Qt::Key_Enter || key == Qt::Key_Return || key == Qt::Key_Tab) {
if (_selected >= 0 && _selected < _rows.size()) {
triggered.notify(_rows[_selected].replacement(), true);
}
return;
}
if ((key != Qt::Key_Up && key != Qt::Key_Down) || _rows.size() < 1) {
return;
}
auto delta = (key == Qt::Key_Down ? 1 : -1), start = _selected;
if (start < 0 || start >= _rows.size()) {
start = (delta > 0) ? (_rows.size() - 1) : 0;
}
auto newSelected = start + delta;
if (newSelected < 0) {
newSelected += _rows.size();
} else if (newSelected >= _rows.size()) {
newSelected -= _rows.size();
}
_mouseSelection = false;
setSelected(newSelected);
}
void SuggestionsWidget::setSelected(int selected) {
if (selected >= _rows.size()) {
selected = -1;
}
if (_selected != selected) {
updateSelectedItem();
_selected = selected;
updateSelectedItem();
}
}
void SuggestionsWidget::setPressed(int pressed) {
if (pressed >= _rows.size()) {
pressed = -1;
}
if (_pressed != pressed) {
_pressed = pressed;
}
}
void SuggestionsWidget::clearMouseSelection() {
if (_mouseSelection) {
clearSelection();
}
}
void SuggestionsWidget::clearSelection() {
_mouseSelection = false;
setSelected(-1);
}
int SuggestionsWidget::itemTop(int index) {
if (index > _rows.size()) {
index = _rows.size();
}
return _st->skip + (_rowHeight * index);
}
void SuggestionsWidget::updateItem(int index) {
if (index >= 0 && index < _rows.size()) {
update(0, itemTop(index), width(), _rowHeight);
}
}
void SuggestionsWidget::updateSelectedItem() {
updateItem(_selected);
}
void SuggestionsWidget::mouseMoveEvent(QMouseEvent *e) {
auto inner = rect().marginsRemoved(QMargins(0, _st->skip, 0, _st->skip));
auto localPosition = e->pos();
if (inner.contains(localPosition)) {
_mouseSelection = true;
updateSelection(e->globalPos());
} else {
clearMouseSelection();
}
}
void SuggestionsWidget::updateSelection(QPoint globalPosition) {
if (!_mouseSelection) return;
auto p = mapFromGlobal(globalPosition) - QPoint(0, _st->skip);
auto selected = (p.y() >= 0) ? (p.y() / _rowHeight) : -1;
setSelected((selected >= 0 && selected < _rows.size()) ? selected : -1);
}
void SuggestionsWidget::mousePressEvent(QMouseEvent *e) {
if (!_mouseSelection) {
return;
}
if (_selected >= 0 && _selected < _rows.size()) {
setPressed(_selected);
if (!_rows[_pressed].ripple()) {
auto mask = RippleAnimation::rectMask(QSize(width(), _rowHeight));
_rows[_pressed].setRipple(std::make_unique<RippleAnimation>(_st->ripple, std::move(mask), [this, selected = _pressed] {
updateItem(selected);
}));
}
_rows[_pressed].ripple()->add(mapFromGlobal(QCursor::pos()) - QPoint(0, itemTop(_pressed)));
}
}
void SuggestionsWidget::mouseReleaseEvent(QMouseEvent *e) {
if (_pressed >= 0 && _pressed < _rows.size()) {
auto pressed = _pressed;
setPressed(-1);
if (_rows[pressed].ripple()) {
_rows[pressed].ripple()->lastStop();
}
if (pressed == _selected) {
triggered.notify(_rows[_selected].replacement(), true);
}
}
}
void SuggestionsWidget::enterEventHook(QEvent *e) {
auto mouse = QCursor::pos();
if (!rect().marginsRemoved(QMargins(0, _st->skip, 0, _st->skip)).contains(mapFromGlobal(mouse))) {
clearMouseSelection();
}
return TWidget::enterEventHook(e);
}
void SuggestionsWidget::leaveEventHook(QEvent *e) {
clearMouseSelection();
return TWidget::leaveEventHook(e);
}
SuggestionsController::SuggestionsController(QWidget *parent, gsl::not_null<QTextEdit*> field) : QObject(nullptr)
, _field(field)
, _container(parent, st::emojiSuggestionsDropdown)
, _suggestions(_container->setOwnedWidget(object_ptr<Ui::Emoji::SuggestionsWidget>(parent, st::emojiSuggestionsMenu))) {
_container->setAutoHiding(false);
_field->installEventFilter(this);
connect(_field, &QTextEdit::textChanged, this, [this] { handleTextChange(); });
connect(_field, &QTextEdit::cursorPositionChanged, this, [this] { handleCursorPositionChange(); });
subscribe(_suggestions->toggleAnimated, [this](bool visible) { suggestionsUpdated(visible); });
subscribe(_suggestions->triggered, [this](QString replacement) { replaceCurrent(replacement); });
updateForceHidden();
handleTextChange();
}
void SuggestionsController::handleTextChange() {
_ignoreCursorPositionChange = true;
InvokeQueued(this, [this] { _ignoreCursorPositionChange = false; });
auto query = getEmojiQuery();
if (query.isEmpty() || _textChangeAfterKeyPress) {
_suggestions->showWithQuery(query);
}
}
QString SuggestionsController::getEmojiQuery() {
if (!cReplaceEmojis()) {
return QString();
}
auto cursor = _field->textCursor();
auto position = _field->textCursor().position();
if (cursor.anchor() != position) {
return QString();
}
auto findTextPart = [this, &position] {
auto document = _field->document();
auto block = document->findBlock(position);
for (auto i = block.begin(); !i.atEnd(); ++i) {
auto fragment = i.fragment();
if (!fragment.isValid()) continue;
auto from = fragment.position();
auto till = from + fragment.length();
if (from >= position || till < position) {
continue;
}
if (fragment.charFormat().isImageFormat()) {
continue;
}
position -= from;
_queryStartPosition = from;
return fragment.text();
}
return QString();
};
auto text = findTextPart();
if (text.isEmpty()) {
return QString();
}
auto isSuggestionChar = [](QChar ch) {
return (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '_') || (ch == '-') || (ch == '+');
};
auto isGoodCharBeforeSuggestion = [isSuggestionChar](QChar ch) {
return !isSuggestionChar(ch) || (ch == 0);
};
t_assert(position > 0 && position <= text.size());
for (auto i = position; i != 0;) {
auto ch = text[--i];
if (ch == ':') {
auto beforeColon = (i < 1) ? QChar(0) : text[i - 1];
if (isGoodCharBeforeSuggestion(beforeColon)) {
// At least one letter after colon.
if (position > i + 1) {
// Skip colon and the first letter.
_queryStartPosition += i + 2;
return text.mid(i, position - i);
}
}
return QString();
}
if (position - i > kSuggestionMaxLength) {
return QString();
}
if (!isSuggestionChar(ch)) {
return QString();
}
}
return QString();
}
void SuggestionsController::replaceCurrent(const QString &replacement) {
auto cursor = _field->textCursor();
auto suggestion = getEmojiQuery();
if (suggestion.isEmpty()) {
_suggestions->showWithQuery(QString());
} else {
cursor.setPosition(cursor.position() - suggestion.size(), QTextCursor::KeepAnchor);
cursor.insertText(replacement + ' ');
}
auto emojiText = GetSuggestionEmoji(QStringToUTF16(replacement));
if (auto emoji = Find(QStringFromUTF16(emojiText))) {
if (emoji->hasVariants()) {
auto it = cEmojiVariants().constFind(emoji->nonColoredId());
if (it != cEmojiVariants().cend()) {
emoji = emoji->variant(it.value());
}
}
AddRecent(emoji);
}
}
void SuggestionsController::handleCursorPositionChange() {
InvokeQueued(this, [this] {
if (_ignoreCursorPositionChange) {
return;
}
_suggestions->showWithQuery(QString());
});
}
void SuggestionsController::suggestionsUpdated(bool visible) {
_shown = visible;
if (_shown) {
_container->resizeToContent();
updateGeometry();
if (!_forceHidden) {
_container->showAnimated(Ui::PanelAnimation::Origin::BottomLeft);
}
} else if (!_forceHidden) {
_container->hideAnimated();
}
}
void SuggestionsController::updateGeometry() {
auto cursor = _field->textCursor();
cursor.setPosition(_queryStartPosition);
auto aroundRect = _field->cursorRect(cursor);
aroundRect.setTopLeft(_field->viewport()->mapToGlobal(aroundRect.topLeft()));
aroundRect.setTopLeft(_container->parentWidget()->mapFromGlobal(aroundRect.topLeft()));
auto boundingRect = _container->parentWidget()->rect();
auto origin = rtl() ? PanelAnimation::Origin::BottomRight : PanelAnimation::Origin::BottomLeft;
auto point = rtl() ? (aroundRect.topLeft() + QPoint(aroundRect.width(), 0)) : aroundRect.topLeft();
point -= rtl() ? QPoint(_container->width() - st::emojiSuggestionsDropdown.padding.right(), _container->height()) : QPoint(st::emojiSuggestionsDropdown.padding.left(), _container->height());
if (rtl()) {
if (point.x() < boundingRect.x()) {
point.setX(boundingRect.x());
}
if (point.x() + _container->width() > boundingRect.x() + boundingRect.width()) {
point.setX(boundingRect.x() + boundingRect.width() - _container->width());
}
} else {
if (point.x() + _container->width() > boundingRect.x() + boundingRect.width()) {
point.setX(boundingRect.x() + boundingRect.width() - _container->width());
}
if (point.x() < boundingRect.x()) {
point.setX(boundingRect.x());
}
}
if (point.y() < boundingRect.y()) {
point.setY(aroundRect.y() + aroundRect.height());
origin = (origin == PanelAnimation::Origin::BottomRight) ? PanelAnimation::Origin::TopRight : PanelAnimation::Origin::TopLeft;
}
_container->move(point);
}
void SuggestionsController::updateForceHidden() {
_forceHidden = !_field->isVisible();
if (_forceHidden) {
_container->hideFast();
} else if (_shown) {
_container->showFast();
}
}
bool SuggestionsController::eventFilter(QObject *object, QEvent *event) {
if (object == _field) {
auto type = event->type();
switch (type) {
case QEvent::Move:
case QEvent::Resize: {
if (_shown) {
updateGeometry();
}
} break;
case QEvent::Show:
case QEvent::ShowToParent:
case QEvent::Hide:
case QEvent::HideToParent: {
updateForceHidden();
} break;
case QEvent::KeyPress: {
auto key = static_cast<QKeyEvent*>(event)->key();
switch (key) {
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Tab:
case Qt::Key_Up:
case Qt::Key_Down:
if (_shown && !_forceHidden) {
_suggestions->handleKeyEvent(key);
return true;
}
break;
case Qt::Key_Escape:
if (_shown && !_forceHidden) {
_suggestions->showWithQuery(QString());
return true;
}
break;
}
_textChangeAfterKeyPress = true;
InvokeQueued(this, [this] { _textChangeAfterKeyPress = false; });
} break;
}
}
return QObject::eventFilter(object, event);
}
void SuggestionsController::raise() {
_container->raise();
}
} // namespace Emoji
} // namespace Ui

View File

@@ -0,0 +1,108 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/effects/panel_animation.h"
namespace Ui {
class InnerDropdown;
class FlatTextarea;
namespace Emoji {
class SuggestionsWidget : public TWidget {
public:
SuggestionsWidget(QWidget *parent, const style::Menu &st);
void showWithQuery(const QString &query);
void handleKeyEvent(int key);
base::Observable<bool> toggleAnimated;
base::Observable<QString> triggered;
protected:
void paintEvent(QPaintEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void enterEventHook(QEvent *e) override;
void leaveEventHook(QEvent *e) override;
private:
class Row;
std::vector<Row> getRowsByQuery() const;
void resizeToRows();
int countWidth(const Row &row);
void setSelected(int selected);
void setPressed(int pressed);
void clearMouseSelection();
void clearSelection();
void updateSelectedItem();
int itemTop(int index);
void updateItem(int index);
void updateSelection(QPoint globalPosition);
gsl::not_null<const style::Menu*> _st;
QString _query;
std::vector<Row> _rows;
int _rowHeight = 0;
bool _mouseSelection = false;
int _selected = -1;
int _pressed = -1;
};
class SuggestionsController : public QObject, private base::Subscriber {
public:
SuggestionsController(QWidget *parent, gsl::not_null<QTextEdit*> field);
void raise();
protected:
bool eventFilter(QObject *object, QEvent *event) override;
private:
void handleCursorPositionChange();
void handleTextChange();
QString getEmojiQuery();
void suggestionsUpdated(bool visible);
void updateGeometry();
void updateForceHidden();
void replaceCurrent(const QString &replacement);
bool _shown = false;
bool _forceHidden = false;
int _queryStartPosition = 0;
bool _ignoreCursorPositionChange = false;
bool _textChangeAfterKeyPress = false;
QPointer<QTextEdit> _field;
object_ptr<InnerDropdown> _container;
QPointer<SuggestionsWidget> _suggestions;
};
} // namespace Emoji
} // namespace Ui

View File

@@ -99,7 +99,7 @@ void FieldAutocomplete::showFiltered(PeerData *peer, QString query, bool addInli
bool resetScroll = (_type != type || _filter != plainQuery);
if (resetScroll) {
_type = type;
_filter = textAccentFold(plainQuery.toString());
_filter = TextUtilities::RemoveAccents(plainQuery.toString());
}
_addInlineBots = addInlineBots;

Some files were not shown because too many files have changed in this diff Show More