Browse Source

Support edit messages

Closes #1155
master
Igor Zhukov 8 years ago
parent
commit
5e855779b9
  1. 126
      app/js/controllers.js
  2. 61
      app/js/directives.js
  3. 5
      app/js/locales/en-us.json
  4. 8
      app/js/message_composer.js
  5. 132
      app/js/messages_manager.js
  6. 15
      app/js/services.js
  7. 9
      app/less/app.less
  8. 8
      app/less/desktop.less
  9. 4
      app/less/mobile.less
  10. 8
      app/partials/desktop/im.html
  11. 5
      app/partials/desktop/message.html
  12. 5
      app/partials/desktop/reply_message.html
  13. 4
      app/partials/mobile/im.html
  14. 5
      app/partials/mobile/message.html
  15. 1
      app/partials/mobile/message_actions_modal.html

126
app/js/controllers.js

@ -502,6 +502,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
channelActions: false, channelActions: false,
canReply: false, canReply: false,
canDelete: false, canDelete: false,
canEdit: false,
actions: function () { actions: function () {
return $scope.historyState.selectActions ? 'selected' : ($scope.historyState.botActions ? 'bot' : ($scope.historyState.channelActions ? 'channel' : false)) return $scope.historyState.selectActions ? 'selected' : ($scope.historyState.botActions ? 'bot' : ($scope.historyState.channelActions ? 'channel' : false))
}, },
@ -1130,6 +1131,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.selectedDelete = selectedDelete $scope.selectedDelete = selectedDelete
$scope.selectedForward = selectedForward $scope.selectedForward = selectedForward
$scope.selectedReply = selectedReply $scope.selectedReply = selectedReply
$scope.selectedEdit = selectedEdit
$scope.selectedCancel = selectedCancel $scope.selectedCancel = selectedCancel
$scope.selectedFlush = selectedFlush $scope.selectedFlush = selectedFlush
$scope.selectInlineBot = selectInlineBot $scope.selectInlineBot = selectInlineBot
@ -1610,8 +1612,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
} }
if (target.className && if (target.className &&
target.className.indexOf('im_message_date') != -1) { target.className.indexOf('im_message_date') != -1) {
if (AppPeersManager.isChannel(peerID) && if (AppPeersManager.isBroadcast(peerID)) {
!AppPeersManager.isMegagroup(peerID)) {
quickForward(messageID) quickForward(messageID)
} else { } else {
selectedReply(messageID) selectedReply(messageID)
@ -1630,6 +1631,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
} }
if (Config.Mobile) { if (Config.Mobile) {
$scope.historyState.canEdit = AppMessagesManager.canEditMessage(messageID)
$modal.open({ $modal.open({
templateUrl: templateUrl('message_actions_modal'), templateUrl: templateUrl('message_actions_modal'),
windowClass: 'message_actions_modal_window', windowClass: 'message_actions_modal_window',
@ -1640,6 +1643,10 @@ angular.module('myApp.controllers', ['myApp.i18n'])
selectedReply(messageID) selectedReply(messageID)
break break
case 'edit':
selectedEdit(messageID)
break
case 'delete': case 'delete':
selectedDelete(messageID) selectedDelete(messageID)
break break
@ -1704,6 +1711,11 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$broadcast('ui_panel_update') $scope.$broadcast('ui_panel_update')
} }
} }
if ($scope.selectedCount == 1) {
angular.forEach($scope.selectedMsgs, function (t, messageID) {
$scope.historyState.canEdit = AppMessagesManager.canEditMessage(messageID)
})
}
$scope.$broadcast('messages_select') $scope.$broadcast('messages_select')
} }
@ -1813,6 +1825,18 @@ angular.module('myApp.controllers', ['myApp.i18n'])
} }
} }
function selectedEdit (selectedMessageID) {
if (!selectedMessageID && $scope.selectedCount == 1) {
angular.forEach($scope.selectedMsgs, function (t, messageID) {
selectedMessageID = messageID
})
}
if (selectedMessageID) {
selectedCancel()
$scope.$broadcast('edit_selected', selectedMessageID)
}
}
function toggleEdit () { function toggleEdit () {
if ($scope.historyState.selectActions) { if ($scope.historyState.selectActions) {
selectedCancel() selectedCancel()
@ -2202,13 +2226,18 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$on('reply_selected', function (e, messageID) { $scope.$on('reply_selected', function (e, messageID) {
replySelect(messageID, true) replySelect(messageID, true)
}) })
$scope.$on('edit_selected', function (e, messageID) {
setEditDraft(messageID, true)
})
$scope.$on('ui_typing', onTyping) $scope.$on('ui_typing', onTyping)
$scope.draftMessage = { $scope.draftMessage = {
text: '', text: '',
send: sendMessage, send: submitMessage,
replyClear: replyClear, replyClear: replyClear,
fwdsClear: fwdsClear fwdsClear: fwdsClear,
type: 'new'
} }
$scope.mentions = {} $scope.mentions = {}
$scope.commands = {} $scope.commands = {}
@ -2232,6 +2261,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$on('inline_bots_popular', updateMentions) $scope.$on('inline_bots_popular', updateMentions)
$scope.$on('last_message_edit', setEditLastMessage)
$scope.replyKeyboardToggle = replyKeyboardToggle $scope.replyKeyboardToggle = replyKeyboardToggle
$scope.toggleSlash = toggleSlash $scope.toggleSlash = toggleSlash
@ -2257,11 +2288,23 @@ angular.module('myApp.controllers', ['myApp.i18n'])
var replyToMarkup = false var replyToMarkup = false
var forceDraft = false var forceDraft = false
var editMessageID = false
function sendMessage (e) { function submitMessage (e) {
$scope.$broadcast('ui_message_before_send') $scope.$broadcast('ui_message_before_send')
$timeout(function () { $timeout(function () {
if (editMessageID) {
editMessage()
} else {
sendMessage()
}
})
return cancelEvent(e)
}
function sendMessage() {
var text = $scope.draftMessage.text var text = $scope.draftMessage.text
if (angular.isString(text) && text.length > 0) { if (angular.isString(text) && text.length > 0) {
@ -2286,9 +2329,17 @@ angular.module('myApp.controllers', ['myApp.i18n'])
resetDraft() resetDraft()
$scope.$broadcast('ui_message_send') $scope.$broadcast('ui_message_send')
}) }
return cancelEvent(e) function editMessage() {
var text = $scope.draftMessage.text
AppMessagesManager.editMessage(editMessageID, text).then(function () {
editMessageID = false
resetDraft()
$scope.$broadcast('ui_message_send')
})
} }
function updateMentions () { function updateMentions () {
@ -2402,6 +2453,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
return return
} }
editMessageID = false
updateMentions() updateMentions()
updateCommands() updateCommands()
replyClear() replyClear()
@ -2413,7 +2466,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
// console.log(dT(), 'reset draft', $scope.curDialog.peer, forceDraft) // console.log(dT(), 'reset draft', $scope.curDialog.peer, forceDraft)
if (forceDraft) { if (forceDraft) {
if (forceDraft == $scope.curDialog.peer) { if (forceDraft == $scope.curDialog.peer) {
$scope.draftMessage.isBroadcast = AppPeersManager.isChannel($scope.curDialog.peerID) && !AppPeersManager.isMegagroup($scope.curDialog.peerID) $scope.draftMessage.isBroadcast = AppPeersManager.isBroadcast($scope.curDialog.peerID)
$scope.$broadcast('ui_peer_draft') $scope.$broadcast('ui_peer_draft')
return return
} else { } else {
@ -2427,9 +2480,25 @@ angular.module('myApp.controllers', ['myApp.i18n'])
function getDraft () { function getDraft () {
if ($scope.curDialog.peerID) { if ($scope.curDialog.peerID) {
DraftsManager.getDraft($scope.curDialog.peerID).then(function (draftData) { var draftDataPromise
if (editMessageID) {
draftDataPromise = AppMessagesManager.getMessageEditData(editMessageID).then(function (draftData) {
draftData.replyToMsgID = editMessageID
return draftData
}, function (error) {
console.warn(error)
editMessageID = false
getDraft()
return $q.reject()
})
} else {
draftDataPromise = DraftsManager.getDraft($scope.curDialog.peerID)
}
draftDataPromise.then(function (draftData) {
console.warn('draft', editMessageID, draftData)
$scope.draftMessage.type = editMessageID ? 'edit' : 'new'
$scope.draftMessage.text = draftData ? draftData.text : '' $scope.draftMessage.text = draftData ? draftData.text : ''
$scope.draftMessage.isBroadcast = AppPeersManager.isChannel($scope.curDialog.peerID) && !AppPeersManager.isMegagroup($scope.curDialog.peerID) $scope.draftMessage.isBroadcast = AppPeersManager.isBroadcast($scope.curDialog.peerID)
if (draftData.replyToMsgID) { if (draftData.replyToMsgID) {
var replyToMsgID = draftData.replyToMsgID var replyToMsgID = draftData.replyToMsgID
replySelect(replyToMsgID) replySelect(replyToMsgID)
@ -2493,11 +2562,15 @@ angular.module('myApp.controllers', ['myApp.i18n'])
} }
function replySelect (messageID, byUser) { function replySelect (messageID, byUser) {
if (editMessageID && byUser) {
replyClear()
return
}
$scope.draftMessage.replyToMsgID = messageID $scope.draftMessage.replyToMsgID = messageID
$scope.$broadcast('ui_peer_reply') $scope.$broadcast('ui_peer_reply')
replyToMarkup = false replyToMarkup = false
if (byUser) { if (byUser && !editMessageID) {
DraftsManager.changeDraft($scope.curDialog.peerID, { DraftsManager.changeDraft($scope.curDialog.peerID, {
text: $scope.draftMessage.text, text: $scope.draftMessage.text,
replyToMsgID: messageID replyToMsgID: messageID
@ -2505,7 +2578,33 @@ angular.module('myApp.controllers', ['myApp.i18n'])
} }
} }
function setEditDraft(messageID) {
editMessageID = messageID
getDraft()
}
function setEditLastMessage() {
if (editMessageID ||
!$scope.curDialog.peerID) {
return false
}
AppMessagesManager.getHistory($scope.curDialog.peerID).then(function (historyResult) {
for (var i = 0, messageID; i < historyResult.history.length; i++) {
messageID = historyResult.history[i]
if (AppMessagesManager.canEditMessage(messageID)) {
setEditDraft(messageID)
break
}
}
})
}
function replyClear (byUser) { function replyClear (byUser) {
if (editMessageID) {
editMessageID = false
getDraft()
return
}
var mid = $scope.draftMessage.replyToMsgID var mid = $scope.draftMessage.replyToMsgID
if (mid && if (mid &&
$scope.historyState.replyKeyboard && $scope.historyState.replyKeyboard &&
@ -2608,6 +2707,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
} }
} }
if ($scope.curDialog.peerID) { if ($scope.curDialog.peerID) {
if (!editMessageID) {
var replyToMsgID = $scope.draftMessage.replyToMsgID var replyToMsgID = $scope.draftMessage.replyToMsgID
if (replyToMsgID && if (replyToMsgID &&
$scope.historyState.replyKeyboard && $scope.historyState.replyKeyboard &&
@ -2618,6 +2718,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
text: newVal, text: newVal,
replyToMsgID: replyToMsgID replyToMsgID: replyToMsgID
}) })
}
checkInlinePattern(newVal) checkInlinePattern(newVal)
} }
} }
@ -2685,8 +2786,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
} }
function onTyping () { function onTyping () {
if (AppPeersManager.isChannel($scope.curDialog.peerID) && if (AppPeersManager.isBroadcast($scope.curDialog.peerID)) {
!AppPeersManager.isMegagroup($scope.curDialog.peerID)) {
return false return false
} }
MtpApiManager.invokeApi('messages.setTyping', { MtpApiManager.invokeApi('messages.setTyping', {

61
app/js/directives.js

@ -527,16 +527,19 @@ angular.module('myApp.directives', ['myApp.filters'])
function link ($scope, element, attrs) { function link ($scope, element, attrs) {
if (attrs.watch) { if (attrs.watch) {
$scope.$parent.$watch(attrs.myReplyMessage, function (mid) { $scope.$parent.$watch(attrs.myReplyMessage, function (mid) {
checkMessage($scope, element, mid) var isEdit = $scope.$parent.$eval(attrs.edit)
checkMessage($scope, element, mid, isEdit)
}) })
} else { } else {
var mid = $scope.$parent.$eval(attrs.myReplyMessage) var mid = $scope.$parent.$eval(attrs.myReplyMessage)
checkMessage($scope, element, mid) var isEdit = $scope.$parent.$eval(attrs.edit)
checkMessage($scope, element, mid, isEdit)
} }
} }
function checkMessage ($scope, element, mid) { function checkMessage ($scope, element, mid, isEdit) {
var message = $scope.replyMessage = AppMessagesManager.wrapSingleMessage(mid) var message = $scope.replyMessage = AppMessagesManager.wrapSingleMessage(mid)
$scope.isEdit = isEdit || false
if (message.loading) { if (message.loading) {
var stopWaiting = $scope.$on('messages_downloaded', function (e, mids) { var stopWaiting = $scope.$on('messages_downloaded', function (e, mids) {
if (mids.indexOf(mid) != -1) { if (mids.indexOf(mid) != -1) {
@ -667,6 +670,49 @@ angular.module('myApp.directives', ['myApp.filters'])
} }
}) })
.directive('myMessageEdited', function (_, $timeout, AppMessagesManager) {
var editedLabel = _('message_edited')
return {
scope: {},
link: link
}
function link($scope, element, attrs) {
var messageID = $scope.$parent.$eval(attrs.myMessageEdited)
console.log(attrs.myMessageEdited, messageID)
if (checkEdited($scope, element, messageID)) {
$scope.$on('message_edit', function (e, data) {
var messageID = $scope.$parent.$eval(attrs.myMessageEdited)
if (data.mid == messageID) {
checkEdited($scope, element, messageID)
}
})
}
}
function checkEdited($scope, element, messageID) {
var message = AppMessagesManager.getMessage(messageID)
console.warn('check edited', messageID, message.canBeEdited, message.edit_date)
if (!message.canBeEdited) {
$timeout(function () {
$scope.$destroy()
element.remove()
})
return false
}
if (message.edit_date) {
element.html(editedLabel).show()
$timeout(function () {
$scope.$destroy()
})
return false
}
return true
}
})
.directive('myDialogs', function ($modalStack, $transition, $window, $timeout) { .directive('myDialogs', function ($modalStack, $transition, $window, $timeout) {
return { return {
link: link link: link
@ -1528,6 +1574,7 @@ angular.module('myApp.directives', ['myApp.filters'])
mentions: $scope.mentions, mentions: $scope.mentions,
commands: $scope.commands, commands: $scope.commands,
onMessageSubmit: onMessageSubmit, onMessageSubmit: onMessageSubmit,
onDirectionKey: onDirectionKey,
onInlineResultSend: onInlineResultSend, onInlineResultSend: onInlineResultSend,
onFilePaste: onFilePaste, onFilePaste: onFilePaste,
onCommandSend: function (command) { onCommandSend: function (command) {
@ -1601,6 +1648,14 @@ angular.module('myApp.directives', ['myApp.filters'])
}) })
} }
function onDirectionKey(e) {
if (e.keyCode == 38) {
$scope.$emit('last_message_edit')
return cancelEvent(e)
}
return true
}
function updateValue () { function updateValue () {
if (richTextarea) { if (richTextarea) {
composer.onChange() composer.onChange()

5
app/js/locales/en-us.json

@ -361,6 +361,7 @@
"message_service_scored_X": "{'one': 'scored {}', 'other': 'scored {}'}", "message_service_scored_X": "{'one': 'scored {}', 'other': 'scored {}'}",
"message_action_reply": "Reply", "message_action_reply": "Reply",
"message_action_edit": "Edit",
"message_action_delete": "Delete", "message_action_delete": "Delete",
"message_action_forward": "Forward", "message_action_forward": "Forward",
"message_action_select": "Select", "message_action_select": "Select",
@ -485,6 +486,7 @@
"im_delete": "Delete {count}", "im_delete": "Delete {count}",
"im_forward": "Forward {count}", "im_forward": "Forward {count}",
"im_reply": "Reply", "im_reply": "Reply",
"im_edit": "Edit",
"im_start": "Start", "im_start": "Start",
"im_channel_join": "+ Join", "im_channel_join": "+ Join",
"im_channel_mute": "Mute", "im_channel_mute": "Mute",
@ -501,6 +503,8 @@
"im_attach_file_title": "Send file", "im_attach_file_title": "Send file",
"im_emoji_btn_title": "Insert emoticon", "im_emoji_btn_title": "Insert emoticon",
"im_submit_message": "Send", "im_submit_message": "Send",
"im_submit_edit_message": "Save",
"im_edit_message_title": "Edit message",
"login_sign_in": "Sign in", "login_sign_in": "Sign in",
"login_enter_number_description": "Please choose your country and enter your full phone number.", "login_enter_number_description": "Please choose your country and enter your full phone number.",
@ -556,6 +560,7 @@
"message_forwarded_message": "Forwarded message", "message_forwarded_message": "Forwarded message",
"message_via_bot": "via {bot}", "message_via_bot": "via {bot}",
"message_edited": "edited",
"message_forwarded_message_mobile": "Forwarded from {from}", "message_forwarded_message_mobile": "Forwarded from {from}",
"message_forwarded_via_message_mobile": "Forwarded from {from} via {bot}", "message_forwarded_via_message_mobile": "Forwarded from {from} via {bot}",

8
app/js/message_composer.js

@ -708,6 +708,7 @@ function MessageComposer (textarea, options) {
this.onTyping = options.onTyping this.onTyping = options.onTyping
this.onMessageSubmit = options.onMessageSubmit this.onMessageSubmit = options.onMessageSubmit
this.onDirectionKey = options.onDirectionKey
this.getSendOnEnter = options.getSendOnEnter this.getSendOnEnter = options.getSendOnEnter
this.onFilePaste = options.onFilePaste this.onFilePaste = options.onFilePaste
this.onCommandSend = options.onCommandSend this.onCommandSend = options.onCommandSend
@ -941,6 +942,13 @@ MessageComposer.prototype.onKeyEvent = function (e) {
return cancelEvent(e) return cancelEvent(e)
} }
} }
// Control keys when content is empty
if ([33, 34, 35, 36, 38, 39].indexOf(e.keyCode) != -1 &&
this.richTextareaEl &&
!this.richTextareaEl[0].textContent.length) {
return this.onDirectionKey(e)
}
} }
} }

132
app/js/messages_manager.js

@ -877,6 +877,64 @@ angular.module('myApp.services')
return messagesStorage[messageID] || {deleted: true} return messagesStorage[messageID] || {deleted: true}
} }
function canMessageBeEdited(message) {
var goodMedias = ['messageMediaPhoto', 'messageMediaDocument', 'messageMediaWebPage', 'messageMediaPending']
if (message._ != 'message' ||
message.deleted ||
message.fwd_from ||
message.via_bot_id ||
message.media && goodMedias.indexOf(message.media._) == -1 ||
message.fromID && AppUsersManager.isBot(message.fromID)) {
return false
}
return true
}
function canEditMessage(messageID) {
if (messageID <= 0 ||
!messagesStorage[messageID]) {
return false
}
var message = messagesStorage[messageID]
if (!message ||
!message.canBeEdited ||
!message.pFlags.out ||
message.date < tsNow(true) - 2 * 86400) {
return false
}
return true
}
function getMessageEditData(messageID) {
if (!canEditMessage(messageID)) {
return $q.reject()
}
var message = getMessage(messageID)
if (message.media &&
message.media._ != 'messageMediaEmpty' &&
message.media._ != 'messageMediaWebPage') {
return $q.when({
caption: true,
text: typeof message.media.caption === 'string' ? message.media.caption : ''
})
}
var text = typeof message.message === 'string' ? message.message : ''
var entities = RichTextProcessor.parseEntities(text)
var serverEntities = message.entities || []
entities = RichTextProcessor.mergeEntities(entities, serverEntities)
text = RichTextProcessor.wrapDraftText(text, {entities: entities})
return $q.when({
caption: false,
text: text
})
}
function deleteMessages (messageIDs) { function deleteMessages (messageIDs) {
var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(messageIDs) var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(messageIDs)
var promises = [] var promises = []
@ -1126,7 +1184,7 @@ angular.module('myApp.services')
var peerID = getMessagePeer(apiMessage) var peerID = getMessagePeer(apiMessage)
var isChannel = apiMessage.to_id._ == 'peerChannel' var isChannel = apiMessage.to_id._ == 'peerChannel'
var channelID = isChannel ? -peerID : 0 var channelID = isChannel ? -peerID : 0
var isBroadcast = isChannel && !AppChatsManager.isMegagroup(channelID) var isBroadcast = isChannel && AppChatsManager.isBroadcast(channelID)
var mid = AppMessagesIDsManager.getFullMessageID(apiMessage.id, channelID) var mid = AppMessagesIDsManager.getFullMessageID(apiMessage.id, channelID)
apiMessage.mid = mid apiMessage.mid = mid
@ -1266,6 +1324,8 @@ angular.module('myApp.services')
apiMessage.totalEntities = RichTextProcessor.mergeEntities(myEntities, apiEntities, !apiMessage.pending) apiMessage.totalEntities = RichTextProcessor.mergeEntities(myEntities, apiEntities, !apiMessage.pending)
} }
apiMessage.canBeEdited = canMessageBeEdited(apiMessage)
if (!options.isEdited) { if (!options.isEdited) {
messagesStorage[mid] = apiMessage messagesStorage[mid] = apiMessage
} }
@ -1285,18 +1345,7 @@ angular.module('myApp.services')
return return
} }
var sendEntites = entities var sendEntites = getInputEntities(entities)
if (entities.length) {
sendEntites = angular.copy(entities)
angular.forEach(sendEntites, function (entity) {
if (entity._ == 'messageEntityMentionName') {
entity._ = 'inputMessageEntityMentionName'
entity.user_id = AppUsersManager.getUserInput(entity.user_id)
}
})
}
var messageID = tempID-- var messageID = tempID--
var randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)] var randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)]
var randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString() var randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString()
@ -2038,6 +2087,51 @@ angular.module('myApp.services')
return false return false
} }
function getInputEntities(entities) {
var sendEntites = angular.copy(entities)
angular.forEach(sendEntites, function (entity) {
if (entity._ == 'messageEntityMentionName') {
entity._ = 'inputMessageEntityMentionName'
entity.user_id = AppUsersManager.getUserInput(entity.user_id)
}
})
return sendEntites
}
function editMessage(messageID, text) {
if (!angular.isString(text) ||
!canEditMessage(messageID)) {
return $q.reject()
}
var entities = []
text = RichTextProcessor.parseMarkdown(text, entities)
var message = getMessage(messageID)
var peerID = getMessagePeer(message)
var flags = 8 | (1 << 11)
return MtpApiManager.invokeApi('messages.editMessage', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
id: AppMessagesIDsManager.getMessageLocalID(messageID),
message: text,
entities: getInputEntities(entities)
}).then(function (updates) {
ApiUpdatesManager.processUpdateMessage(updates)
}, function (error) {
if (error &&
error.type == 'MESSAGE_NOT_MODIFIED') {
error.handled = true
return
}
if (error &&
error.type == 'MESSAGE_EMPTY') {
error.handled = true
}
return $q.reject(error)
})
}
function getMessagePeer (message) { function getMessagePeer (message) {
var toID = message.to_id && AppPeersManager.getPeerID(message.to_id) || 0 var toID = message.to_id && AppPeersManager.getPeerID(message.to_id) || 0
@ -2250,9 +2344,11 @@ angular.module('myApp.services')
var fromUser = message.from_id && AppUsersManager.getUser(message.from_id) var fromUser = message.from_id && AppUsersManager.getUser(message.from_id)
var fromBot = fromUser && fromUser.pFlags.bot && fromUser.username || false var fromBot = fromUser && fromUser.pFlags.bot && fromUser.username || false
var toPeerID = AppPeersManager.getPeerID(message.to_id) var toPeerID = AppPeersManager.getPeerID(message.to_id)
var withBot = (fromBot || var withBot = (
toPeerID < 0 && !(AppChatsManager.isChannel(-toPeerID) && !AppChatsManager.isMegagroup(-toPeerID)) || fromBot ||
toPeerID > 0 && AppUsersManager.isBot(toPeerID)) AppPeersManager.isBot(toPeerID) ||
AppPeersManager.isAnyGroup(toPeerID)
)
var options = { var options = {
noCommands: !withBot, noCommands: !withBot,
@ -3258,10 +3354,14 @@ angular.module('myApp.services')
forwardMessages: forwardMessages, forwardMessages: forwardMessages,
startBot: startBot, startBot: startBot,
shareGame: shareGame, shareGame: shareGame,
editMessage: editMessage,
convertMigratedPeer: convertMigratedPeer, convertMigratedPeer: convertMigratedPeer,
getMessagePeer: getMessagePeer, getMessagePeer: getMessagePeer,
getMessageThumb: getMessageThumb, getMessageThumb: getMessageThumb,
getMessageShareLink: getMessageShareLink, getMessageShareLink: getMessageShareLink,
canMessageBeEdited: canMessageBeEdited,
canEditMessage: canEditMessage,
getMessageEditData: getMessageEditData,
clearDialogCache: clearDialogCache, clearDialogCache: clearDialogCache,
wrapForDialog: wrapForDialog, wrapForDialog: wrapForDialog,
wrapForHistory: wrapForHistory, wrapForHistory: wrapForHistory,

15
app/js/services.js

@ -709,6 +709,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
return false return false
} }
function isBroadcast (id) {
return isChannel(id) && !isMegagroup(id)
}
function getChatInput (id) { function getChatInput (id) {
return id || 0 return id || 0
} }
@ -827,6 +831,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
getChat: getChat, getChat: getChat,
isChannel: isChannel, isChannel: isChannel,
isMegagroup: isMegagroup, isMegagroup: isMegagroup,
isBroadcast: isBroadcast,
hasRights: hasRights, hasRights: hasRights,
saveChannelAccess: saveChannelAccess, saveChannelAccess: saveChannelAccess,
saveIsMegagroup: saveIsMegagroup, saveIsMegagroup: saveIsMegagroup,
@ -987,6 +992,14 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
return (peerID < 0) && AppChatsManager.isMegagroup(-peerID) return (peerID < 0) && AppChatsManager.isMegagroup(-peerID)
} }
function isAnyGroup (peerID) {
return (peerID < 0) && !AppChatsManager.isBroadcast(-peerID)
}
function isBroadcast (id) {
return isChannel(id) && !isMegagroup(id)
}
function isBot (peerID) { function isBot (peerID) {
return (peerID > 0) && AppUsersManager.isBot(peerID) return (peerID > 0) && AppUsersManager.isBot(peerID)
} }
@ -1002,7 +1015,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
getPeerPhoto: getPeerPhoto, getPeerPhoto: getPeerPhoto,
resolveUsername: resolveUsername, resolveUsername: resolveUsername,
isChannel: isChannel, isChannel: isChannel,
isAnyGroup: isAnyGroup,
isMegagroup: isMegagroup, isMegagroup: isMegagroup,
isBroadcast: isBroadcast,
isBot: isBot isBot: isBot
} }
}) })

9
app/less/app.less

@ -2103,7 +2103,11 @@ img.im_message_document_thumb {
padding: 0 0 20px 10px; padding: 0 0 20px 10px;
} }
.im_message_date { .im_message_edited {
display: none;
}
.im_message_date_text {
cursor: pointer; cursor: pointer;
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
@ -3908,7 +3912,8 @@ ul.chat_modal_migrate_list {
.audio_player_size, .audio_player_size,
.im_message_fwd_date, .im_message_fwd_date,
.im_message_views_cnt, .im_message_views_cnt,
.im_message_sign_link { .im_message_sign_link,
.im_message_edited {
color: #899daf; color: #899daf;
} }

8
app/less/desktop.less

@ -881,6 +881,14 @@ a.footer_link.active:active {
} }
} }
.im_submit_edit_label,
.im_submit_edit .im_submit_send_label {
display: none;
}
.im_submit_edit .im_submit_edit_label {
display: inline;
}
.composer_emoji_panel { .composer_emoji_panel {
display: block; display: block;
height: 30px; height: 30px;

4
app/less/mobile.less

@ -856,6 +856,10 @@ img.im_message_video_thumb,
font-size: 10px; font-size: 10px;
padding: 0; padding: 0;
} }
.im_message_edited {
position: absolute;
top: -11px;
}
.im_message_out { .im_message_out {
.im_message_fwd_date { .im_message_fwd_date {

8
app/partials/desktop/im.html

@ -168,6 +168,7 @@
<a class="btn btn-primary im_edit_forward_btn" ng-click="selectedForward()" ng-class="{disabled: !selectedCount}" ng-disabled="!selectedCount" my-i18n-format="im_forward"></a> <a class="btn btn-primary im_edit_forward_btn" ng-click="selectedForward()" ng-class="{disabled: !selectedCount}" ng-disabled="!selectedCount" my-i18n-format="im_forward"></a>
<a class="btn btn-primary im_edit_delete_btn" ng-click="selectedDelete()" ng-class="{disabled: !selectedCount}" ng-disabled="!selectedCount" my-i18n-format="im_delete" ng-show="historyState.canDelete"></a> <a class="btn btn-primary im_edit_delete_btn" ng-click="selectedDelete()" ng-class="{disabled: !selectedCount}" ng-disabled="!selectedCount" my-i18n-format="im_delete" ng-show="historyState.canDelete"></a>
<a class="btn btn-primary im_edit_reply_btn" ng-click="selectedReply()" ng-show="selectedCount == 1 &amp;&amp; historyState.canReply" my-i18n="im_reply"></a> <a class="btn btn-primary im_edit_reply_btn" ng-click="selectedReply()" ng-show="selectedCount == 1 &amp;&amp; historyState.canReply" my-i18n="im_reply"></a>
<a class="btn btn-primary im_edit_reply_btn" ng-click="selectedEdit()" ng-show="selectedCount == 1 &amp;&amp; historyState.canEdit" my-i18n="im_edit"></a>
<my-i18n-param name="count"><strong class="im_selected_count" ng-show="selectedCount > 0" ng-bind="selectedCount"></strong></my-i18n-param> <my-i18n-param name="count"><strong class="im_selected_count" ng-show="selectedCount > 0" ng-bind="selectedCount"></strong></my-i18n-param>
</div> </div>
</div> </div>
@ -189,7 +190,7 @@
<div class="im_send_reply_wrap" ng-if="draftMessage.replyToMsgID > 0"> <div class="im_send_reply_wrap" ng-if="draftMessage.replyToMsgID > 0">
<a class="im_send_reply_cancel" ng-mousedown="draftMessage.replyClear(true)"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a> <a class="im_send_reply_cancel" ng-mousedown="draftMessage.replyClear(true)"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a>
<a class="im_message_reply_wrap" my-reply-message="draftMessage.replyToMsgID" watch="true"></a> <a class="im_message_reply_wrap" my-reply-message="draftMessage.replyToMsgID" watch="true" edit="{{draftMessage.type == 'edit'}}"></a>
</div> </div>
<div class="im_send_reply_wrap im_send_fwds_wrap" ng-if="draftMessage.fwdMessages.length > 0"> <div class="im_send_reply_wrap im_send_fwds_wrap" ng-if="draftMessage.fwdMessages.length > 0">
@ -210,7 +211,10 @@
</div> </div>
<div class="im_send_buttons_wrap clearfix"> <div class="im_send_buttons_wrap clearfix">
<button type="submit" class="btn btn-md im_submit nocopy" my-i18n="im_submit_message"></button> <button type="submit" class="btn btn-md im_submit nocopy" ng-class="draftMessage.type == 'edit' ? 'im_submit_edit' : 'im_submit_send'">
<span class="im_submit_send_label" my-i18n="im_submit_message"></span>
<span class="im_submit_edit_label" my-i18n="im_submit_edit_message"></span>
</button>
<div class="im_attach pull-left"> <div class="im_attach pull-left">
<input type="file" class="im_attach_input" size="28" multiple="multiple" title="{{'im_attach_file_title' | i18n}}" /> <input type="file" class="im_attach_input" size="28" multiple="multiple" title="{{'im_attach_file_title' | i18n}}" />

5
app/partials/desktop/message.html

@ -38,7 +38,10 @@
<i class="icon-message-views"></i><span class="im_message_views_cnt" my-message-views="historyMessage.mid"></span> <i class="icon-message-views"></i><span class="im_message_views_cnt" my-message-views="historyMessage.mid"></span>
</div> </div>
</div> </div>
<span class="im_message_date clickable nocopy" data-content="{{::historyMessage.date | time}}"></span> <span class="im_message_date clickable">
<span class="im_message_edited" my-message-edited="historyMessage.mid"></span>
<span class="im_message_date_text nocopy" data-content="{{::historyMessage.date | time}}"></span>
</span>
</div> </div>
<div class="im_message_body" ng-class="::{im_message_body_media: historyMessage._ == 'message' &amp;&amp; historyMessage.media ? true : false}"> <div class="im_message_body" ng-class="::{im_message_body_media: historyMessage._ == 'message' &amp;&amp; historyMessage.media ? true : false}">

5
app/partials/desktop/reply_message.html

@ -11,9 +11,10 @@
watch="true" watch="true"
/> />
</div> </div>
<div class="im_message_reply_author" ng-switch-default> <div class="im_message_reply_author" ng-switch-default ng-switch="isEdit">
<span class="copyonly">&gt;&nbsp;</span> <span class="copyonly">&gt;&nbsp;</span>
<span my-peer-link="replyMessage.fromID" peer-watch="true"></span> <span ng-switch-when="true" my-i18n="im_edit_message_title"></span>
<span ng-switch-default my-peer-link="replyMessage.fromID" peer-watch="true"></span>
</div> </div>
<div class="im_message_reply_body" ng-switch-default> <div class="im_message_reply_body" ng-switch-default>
<span class="copyonly">&gt;&nbsp;</span> <span class="copyonly">&gt;&nbsp;</span>

4
app/partials/mobile/im.html

@ -136,11 +136,11 @@
<div class="im_send_form_wrap1"> <div class="im_send_form_wrap1">
<div class="im_send_form_wrap clearfix" ng-controller="AppImSendController"> <div class="im_send_form_wrap clearfix" ng-controller="AppImSendController">
<form my-send-form draft-message="draftMessage" mentions="mentions" commands="commands" class="im_send_form" ng-class="{im_send_form_empty: !draftMessage.text.length, composer_progress_enabled: draftMessage.inlineProgress}"> <form my-send-form draft-message="draftMessage" mentions="mentions" commands="commands" class="im_send_form" ng-class="{im_send_form_empty: !draftMessage.text.length && draftMessage.type != 'edit', composer_progress_enabled: draftMessage.inlineProgress}">
<div class="im_send_reply_wrap" ng-if="draftMessage.replyToMsgID > 0"> <div class="im_send_reply_wrap" ng-if="draftMessage.replyToMsgID > 0">
<a class="im_send_reply_cancel" ng-mousedown="draftMessage.replyClear()"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a> <a class="im_send_reply_cancel" ng-mousedown="draftMessage.replyClear()"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a>
<a class="im_message_reply_wrap" my-reply-message="draftMessage.replyToMsgID" watch="true"></a> <a class="im_message_reply_wrap" my-reply-message="draftMessage.replyToMsgID" watch="true" edit="{{draftMessage.type == 'edit'}}"></a>
</div> </div>
<div class="im_send_reply_wrap im_send_fwds_wrap" ng-if="draftMessage.fwdMessages.length > 0"> <div class="im_send_reply_wrap im_send_fwds_wrap" ng-if="draftMessage.fwdMessages.length > 0">

5
app/partials/mobile/message.html

@ -37,7 +37,10 @@
<i class="icon-message-status" tooltip="{{'message_action_retry' | i18n}}"></i> <i class="icon-message-status" tooltip="{{'message_action_retry' | i18n}}"></i>
</a> </a>
<i ng-if="::historyMessage.pFlags.unread &amp;&amp; historyMessage.pFlags.out || historyMessage.pending || false" class="icon-message-status" ng-show="!historyMessage.error"></i> <i ng-if="::historyMessage.pFlags.unread &amp;&amp; historyMessage.pFlags.out || historyMessage.pending || false" class="icon-message-status" ng-show="!historyMessage.error"></i>
<span class="im_message_date" ng-bind="::historyMessage.date | time"></span> <span class="im_message_date">
<span class="im_message_edited" my-message-edited="historyMessage.mid"></span>
<span class="im_message_date_text" ng-bind="::historyMessage.date | time"></span>
</span>
</div> </div>
<div my-message-body="historyMessage"> <div my-message-body="historyMessage">

1
app/partials/mobile/message_actions_modal.html

@ -2,6 +2,7 @@
<div class="message_actions_wrap"> <div class="message_actions_wrap">
<button ng-if="historyState.canReply" class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_reply" ng-click="$close('reply')"></button> <button ng-if="historyState.canReply" class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_reply" ng-click="$close('reply')"></button>
<button ng-if="historyState.canEdit" class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_edit" ng-click="$close('edit')"></button>
<button class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_forward" ng-click="$close('forward')"></button> <button class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_forward" ng-click="$close('forward')"></button>
<button ng-if="historyState.canDelete" class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_delete" ng-click="$close('delete')"></button> <button ng-if="historyState.canDelete" class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_delete" ng-click="$close('delete')"></button>
<button class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_select" ng-click="$close('select')"></button> <button class="btn btn-md btn-md-primary btn-block" my-i18n="message_action_select" ng-click="$close('select')"></button>

Loading…
Cancel
Save