Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
AloqaIM-Android
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
AloqaIM-Android
Commits
b5238704
Commit
b5238704
authored
Apr 10, 2018
by
Leonardo Aramaki
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add possibility to send offline messages
parent
5b1ce1b6
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
163 additions
and
62 deletions
+163
-62
AndroidManifest.xml
app/src/main/AndroidManifest.xml
+7
-2
ChatRoomAdapter.kt
...a/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
+2
-0
MessageViewHolder.kt
...chat/rocket/android/chatroom/adapter/MessageViewHolder.kt
+7
-3
MessageServiceProvider.kt
...chat/rocket/android/chatroom/di/MessageServiceProvider.kt
+11
-0
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+61
-30
ChatRoomView.kt
...chat/rocket/android/chatroom/presentation/ChatRoomView.kt
+1
-3
MessageService.kt
...va/chat/rocket/android/chatroom/service/MessageService.kt
+31
-0
ChatRoomFragment.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
+6
-6
AudioAttachmentViewModel.kt
...et/android/chatroom/viewmodel/AudioAttachmentViewModel.kt
+2
-1
AuthorAttachmentViewModel.kt
...t/android/chatroom/viewmodel/AuthorAttachmentViewModel.kt
+12
-11
BaseViewModel.kt
...a/chat/rocket/android/chatroom/viewmodel/BaseViewModel.kt
+1
-0
ImageAttachmentViewModel.kt
...et/android/chatroom/viewmodel/ImageAttachmentViewModel.kt
+2
-1
MessageAttachmentViewModel.kt
.../android/chatroom/viewmodel/MessageAttachmentViewModel.kt
+2
-1
MessageViewModel.kt
...hat/rocket/android/chatroom/viewmodel/MessageViewModel.kt
+2
-1
UrlPreviewViewModel.kt
.../rocket/android/chatroom/viewmodel/UrlPreviewViewModel.kt
+2
-1
VideoAttachmentViewModel.kt
...et/android/chatroom/viewmodel/VideoAttachmentViewModel.kt
+2
-1
AppComponent.kt
app/src/main/java/chat/rocket/android/dagger/AppComponent.kt
+3
-0
ServiceBuilder.kt
.../java/chat/rocket/android/dagger/module/ServiceBuilder.kt
+5
-0
LocalRepository.kt
...ava/chat/rocket/android/infrastructure/LocalRepository.kt
+4
-1
No files found.
app/src/main/AndroidManifest.xml
View file @
b5238704
...
...
@@ -100,13 +100,18 @@
<action
android:name=
"com.google.android.c2dm.intent.RECEIVE"
/>
</intent-filter>
</service>
<service
android:name=
".chatroom.service.MessageService"
android:exported=
"false"
>
</service>
<meta-data
android:name=
"io.fabric.ApiKey"
android:value=
"12ac6e94f850aaffcdff52001af77ca415d06a43"
/>
<activity
android:name=
".settings.about.ui.AboutActivity"
android:theme=
"@style/AppTheme"
/>
<activity
android:name=
".settings.about.ui.AboutActivity"
android:theme=
"@style/AppTheme"
/>
</application>
</manifest>
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
View file @
b5238704
...
...
@@ -123,6 +123,8 @@ class ChatRoomAdapter(
if
(
item
==
null
)
{
this
.
dataSet
.
addAll
(
0
,
dataSet
)
notifyItemRangeInserted
(
0
,
dataSet
.
size
)
}
else
{
dataSet
.
forEach
{
updateItem
(
it
)
}
}
}
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt
View file @
b5238704
package
chat.rocket.android.chatroom.adapter
import
android.graphics.Color
import
android.text.method.LinkMovementMethod
import
android.view.View
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
...
...
@@ -8,9 +9,9 @@ import kotlinx.android.synthetic.main.avatar.view.*
import
kotlinx.android.synthetic.main.item_message.view.*
class
MessageViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
,
reactionListener
:
EmojiReactionListener
?
=
null
itemView
:
View
,
listener
:
ActionsListener
,
reactionListener
:
EmojiReactionListener
?
=
null
)
:
BaseViewHolder
<
MessageViewModel
>(
itemView
,
listener
,
reactionListener
)
{
init
{
...
...
@@ -29,6 +30,9 @@ class MessageViewHolder(
text_sender
.
text
=
data
.
senderName
text_content
.
text
=
data
.
content
image_avatar
.
setImageURI
(
data
.
avatar
)
text_content
.
setTextColor
(
if
(
data
.
isTemporary
)
Color
.
GRAY
else
Color
.
BLACK
)
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/di/MessageServiceProvider.kt
0 → 100644
View file @
b5238704
package
chat.rocket.android.chatroom.di
import
chat.rocket.android.chatroom.service.MessageService
import
chat.rocket.android.dagger.module.AppModule
import
dagger.Module
import
dagger.android.ContributesAndroidInjector
@Module
abstract
class
MessageServiceProvider
{
@ContributesAndroidInjector
(
modules
=
[
AppModule
::
class
])
abstract
fun
provideMessageService
():
MessageService
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt
View file @
b5238704
...
...
@@ -12,6 +12,7 @@ import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewMo
import
chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.username
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.server.infraestructure.state
...
...
@@ -20,6 +21,7 @@ import chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.SimpleUser
import
chat.rocket.common.model.UserStatus
import
chat.rocket.common.model.roomTypeOf
import
chat.rocket.common.util.ifNull
...
...
@@ -106,15 +108,42 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
view
.
disableSendMessageButton
()
try
{
// ignore message for now, will receive it on the stream
val
id
=
UUID
.
randomUUID
().
toString
()
val
message
=
retryIO
{
if
(
messageId
==
null
)
{
val
id
=
UUID
.
randomUUID
().
toString
()
val
username
=
localRepository
.
username
()
val
newMessage
=
Message
(
id
=
id
,
roomId
=
chatRoomId
,
message
=
text
,
timestamp
=
Instant
.
now
().
epochSecond
,
sender
=
SimpleUser
(
null
,
username
,
username
),
attachments
=
null
,
avatar
=
username
?.
avatarUrl
(
username
),
channels
=
null
,
editedAt
=
null
,
editedBy
=
null
,
groupable
=
false
,
parseUrls
=
false
,
pinned
=
false
,
mentions
=
emptyList
(),
reactions
=
null
,
senderAlias
=
null
,
type
=
null
,
updatedAt
=
null
,
urls
=
null
)
val
offlineMessage
=
mapper
.
map
(
newMessage
).
map
{
it
.
isTemporary
=
true
it
}
view
.
showNewMessage
(
offlineMessage
)
client
.
sendMessage
(
id
,
chatRoomId
,
text
)
}
else
{
client
.
updateMessage
(
chatRoomId
,
messageId
,
text
)
}
}
view
.
enableSendMessageButton
(
false
)
view
.
enableSendMessageButton
()
}
catch
(
ex
:
Exception
)
{
Timber
.
d
(
ex
,
"Error sending message..."
)
ex
.
message
?.
let
{
...
...
@@ -122,7 +151,8 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
}.
ifNull
{
view
.
showGenericErrorMessage
()
}
view
.
enableSendMessageButton
(
true
)
}
finally
{
view
.
enableSendMessageButton
()
}
}
}
...
...
@@ -214,34 +244,34 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
val
roomType
=
roomTypeOf
(
chatRoomType
!!
)
messagesRepository
.
getByRoomId
(
chatRoomId
!!
)
.
sortedByDescending
{
it
.
timestamp
}.
firstOrNull
()
?.
let
{
lastMessage
->
val
instant
=
Instant
.
ofEpochMilli
(
lastMessage
.
timestamp
).
toString
()
try
{
val
messages
=
retryIO
(
description
=
"history($chatRoomId, $roomType, $instant)"
)
{
client
.
history
(
chatRoomId
!!
,
roomType
,
count
=
50
,
oldest
=
instant
)
}
Timber
.
d
(
"History: $messages"
)
if
(
messages
.
result
.
isNotEmpty
())
{
val
models
=
mapper
.
map
(
messages
.
result
)
messagesRepository
.
saveAll
(
messages
.
result
)
launchUI
(
strategy
)
{
view
.
showNewMessage
(
models
)
}
if
(
messages
.
result
.
size
==
50
)
{
// we loaded at least count messages, try one more to fetch more messages
loadMissingMessages
()
val
instant
=
Instant
.
ofEpochMilli
(
lastMessage
.
timestamp
).
toString
()
try
{
val
messages
=
retryIO
(
description
=
"history($chatRoomId, $roomType, $instant)"
)
{
client
.
history
(
chatRoomId
!!
,
roomType
,
count
=
50
,
oldest
=
instant
)
}
Timber
.
d
(
"History: $messages"
)
if
(
messages
.
result
.
isNotEmpty
())
{
val
models
=
mapper
.
map
(
messages
.
result
)
messagesRepository
.
saveAll
(
messages
.
result
)
launchUI
(
strategy
)
{
view
.
showNewMessage
(
models
)
}
if
(
messages
.
result
.
size
==
50
)
{
// we loaded at least count messages, try one more to fetch more messages
loadMissingMessages
()
}
}
}
catch
(
ex
:
Exception
)
{
// TODO - we need to better treat connection problems here, but no let gaps
// on the messages list
Timber
.
d
(
ex
,
"Error fetching channel history"
)
ex
.
printStackTrace
()
}
}
}
catch
(
ex
:
Exception
)
{
// TODO - we need to better treat connection problems here, but no let gaps
// on the messages list
Timber
.
d
(
ex
,
"Error fetching channel history"
)
ex
.
printStackTrace
()
}
}
}
}
}
...
...
@@ -563,12 +593,13 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
// failed, command is not valid so post it
sendMessage
(
roomId
,
text
,
null
)
}
view
.
enableSendMessageButton
(
false
)
}
}
catch
(
ex
:
RocketChatException
)
{
Timber
.
e
(
ex
)
// command is not valid, post it
sendMessage
(
roomId
,
text
,
null
)
}
finally
{
view
.
enableSendMessageButton
()
}
}
}
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomView.kt
View file @
b5238704
...
...
@@ -92,10 +92,8 @@ interface ChatRoomView : LoadingView, MessageView {
/**
* Enables the send message button.
*
* @param sendFailed Whether the sent message has failed.
*/
fun
enableSendMessageButton
(
sendFailed
:
Boolean
)
fun
enableSendMessageButton
()
/**
* Clears the message composition.
...
...
app/src/main/java/chat/rocket/android/chatroom/service/MessageService.kt
0 → 100644
View file @
b5238704
package
chat.rocket.android.chatroom.service
import
android.app.job.JobParameters
import
android.app.job.JobService
import
chat.rocket.android.server.domain.CurrentServerRepository
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.launch
import
javax.inject.Inject
class
MessageService
:
JobService
()
{
@Inject
lateinit
var
factory
:
ConnectionManagerFactory
@Inject
lateinit
var
currentServerRepository
:
CurrentServerRepository
override
fun
onStopJob
(
params
:
JobParameters
?):
Boolean
{
return
true
}
override
fun
onStartJob
(
params
:
JobParameters
?):
Boolean
{
launch
(
CommonPool
)
{
val
currentServer
=
currentServerRepository
.
get
()
if
(
currentServer
!=
null
)
{
val
connectionManager
=
factory
.
create
(
currentServer
)
}
jobFinished
(
params
,
false
)
}
return
true
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
View file @
b5238704
...
...
@@ -70,8 +70,10 @@ private const val BUNDLE_CHAT_ROOM_LAST_SEEN = "chat_room_last_seen"
private
const
val
BUNDLE_CHAT_ROOM_IS_SUBSCRIBED
=
"chat_room_is_subscribed"
class
ChatRoomFragment
:
Fragment
(),
ChatRoomView
,
EmojiKeyboardListener
,
EmojiReactionListener
{
@Inject
lateinit
var
presenter
:
ChatRoomPresenter
@Inject
lateinit
var
parser
:
MessageParser
@Inject
lateinit
var
presenter
:
ChatRoomPresenter
@Inject
lateinit
var
parser
:
MessageParser
private
lateinit
var
adapter
:
ChatRoomAdapter
private
lateinit
var
chatRoomId
:
String
private
lateinit
var
chatRoomName
:
String
...
...
@@ -310,12 +312,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
button_send
.
isEnabled
=
false
}
override
fun
enableSendMessageButton
(
sendFailed
:
Boolean
)
{
override
fun
enableSendMessageButton
()
{
button_send
.
isEnabled
=
true
text_message
.
isEnabled
=
true
if
(!
sendFailed
)
{
clearMessageComposition
()
}
clearMessageComposition
()
}
override
fun
clearMessageComposition
()
{
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/AudioAttachmentViewModel.kt
View file @
b5238704
...
...
@@ -13,7 +13,8 @@ data class AudioAttachmentViewModel(
override
val
id
:
Long
,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
override
var
preview
:
Message
?
=
null
,
override
var
isTemporary
:
Boolean
=
false
)
:
BaseFileAttachmentViewModel
<
AudioAttachment
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
AUDIO_ATTACHMENT
.
viewType
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/AuthorAttachmentViewModel.kt
View file @
b5238704
...
...
@@ -5,17 +5,18 @@ import chat.rocket.core.model.Message
import
chat.rocket.core.model.attachment.AuthorAttachment
data class
AuthorAttachmentViewModel
(
override
val
attachmentUrl
:
String
,
val
id
:
Long
,
val
name
:
CharSequence
?,
val
icon
:
String
?,
val
fields
:
CharSequence
?,
override
val
message
:
Message
,
override
val
rawData
:
AuthorAttachment
,
override
val
messageId
:
String
,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
override
val
attachmentUrl
:
String
,
val
id
:
Long
,
val
name
:
CharSequence
?,
val
icon
:
String
?,
val
fields
:
CharSequence
?,
override
val
message
:
Message
,
override
val
rawData
:
AuthorAttachment
,
override
val
messageId
:
String
,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
,
override
var
isTemporary
:
Boolean
=
false
)
:
BaseAttachmentViewModel
<
AuthorAttachment
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
AUTHOR_ATTACHMENT
.
viewType
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/BaseViewModel.kt
View file @
b5238704
...
...
@@ -12,6 +12,7 @@ interface BaseViewModel<out T> {
var
reactions
:
List
<
ReactionViewModel
>
var
nextDownStreamMessage
:
BaseViewModel
<*>?
var
preview
:
Message
?
var
isTemporary
:
Boolean
enum
class
ViewType
(
val
viewType
:
Int
)
{
MESSAGE
(
0
),
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/ImageAttachmentViewModel.kt
View file @
b5238704
...
...
@@ -13,7 +13,8 @@ data class ImageAttachmentViewModel(
override
val
id
:
Long
,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
override
var
preview
:
Message
?
=
null
,
override
var
isTemporary
:
Boolean
=
false
)
:
BaseFileAttachmentViewModel
<
ImageAttachment
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
IMAGE_ATTACHMENT
.
viewType
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/MessageAttachmentViewModel.kt
View file @
b5238704
...
...
@@ -14,7 +14,8 @@ data class MessageAttachmentViewModel(
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
var
messageLink
:
String
?
=
null
,
override
var
preview
:
Message
?
=
null
override
var
preview
:
Message
?
=
null
,
override
var
isTemporary
:
Boolean
=
false
)
:
BaseViewModel
<
Message
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
MESSAGE_ATTACHMENT
.
viewType
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/MessageViewModel.kt
View file @
b5238704
...
...
@@ -15,7 +15,8 @@ data class MessageViewModel(
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
,
var
isFirstUnread
:
Boolean
var
isFirstUnread
:
Boolean
,
override
var
isTemporary
:
Boolean
=
false
)
:
BaseMessageViewModel
<
Message
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
MESSAGE
.
viewType
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/UrlPreviewViewModel.kt
View file @
b5238704
...
...
@@ -14,7 +14,8 @@ data class UrlPreviewViewModel(
val
thumbUrl
:
String
?,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
override
var
preview
:
Message
?
=
null
,
override
var
isTemporary
:
Boolean
=
false
)
:
BaseViewModel
<
Url
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
URL_PREVIEW
.
viewType
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/VideoAttachmentViewModel.kt
View file @
b5238704
...
...
@@ -13,7 +13,8 @@ data class VideoAttachmentViewModel(
override
val
id
:
Long
,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
override
var
preview
:
Message
?
=
null
,
override
var
isTemporary
:
Boolean
=
false
)
:
BaseFileAttachmentViewModel
<
VideoAttachment
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
VIDEO_ATTACHMENT
.
viewType
...
...
app/src/main/java/chat/rocket/android/dagger/AppComponent.kt
View file @
b5238704
...
...
@@ -2,6 +2,7 @@ package chat.rocket.android.dagger
import
android.app.Application
import
chat.rocket.android.app.RocketChatApplication
import
chat.rocket.android.chatroom.service.MessageService
import
chat.rocket.android.dagger.module.ActivityBuilder
import
chat.rocket.android.dagger.module.AppModule
import
chat.rocket.android.dagger.module.ReceiverBuilder
...
...
@@ -29,6 +30,8 @@ interface AppComponent {
fun
inject
(
service
:
FirebaseTokenService
)
fun
inject
(
service
:
MessageService
)
/*@Component.Builder
abstract class Builder : AndroidInjector.Builder<RocketChatApplication>()*/
}
app/src/main/java/chat/rocket/android/dagger/module/ServiceBuilder.kt
View file @
b5238704
package
chat.rocket.android.dagger.module
import
chat.rocket.android.chatroom.di.MessageServiceProvider
import
chat.rocket.android.chatroom.service.MessageService
import
chat.rocket.android.push.FirebaseTokenService
import
chat.rocket.android.push.GcmListenerService
import
chat.rocket.android.push.di.FirebaseTokenServiceProvider
...
...
@@ -14,4 +16,7 @@ import dagger.android.ContributesAndroidInjector
@ContributesAndroidInjector
(
modules
=
[
GcmListenerServiceProvider
::
class
])
abstract
fun
bindGcmListenerService
():
GcmListenerService
@ContributesAndroidInjector
(
modules
=
[
MessageServiceProvider
::
class
])
abstract
fun
bindMessageService
():
MessageService
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/infrastructure/LocalRepository.kt
View file @
b5238704
package
chat.rocket.android.infrastructure
import
chat.rocket.core.model.Myself
interface
LocalRepository
{
fun
save
(
key
:
String
,
value
:
String
?)
...
...
@@ -24,4 +26,5 @@ interface LocalRepository {
}
}
fun
LocalRepository
.
checkIfMyself
(
username
:
String
)
=
get
(
LocalRepository
.
CURRENT_USERNAME_KEY
)
==
username
\ No newline at end of file
fun
LocalRepository
.
checkIfMyself
(
username
:
String
)
=
username
()
==
username
fun
LocalRepository
.
username
()
=
get
(
LocalRepository
.
CURRENT_USERNAME_KEY
)
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment