Compare commits

...

17 Commits

Author SHA1 Message Date
Karol Kosek 2ad4d2bf4f Translated using Weblate (Polish)
Currently translated at 79.3% (69 of 87 strings)

Translation: Husky/Husky translations
Translate-URL: https://l10n.mentality.rip/projects/husky/husky-translations/pl/
2020-10-09 18:12:14 +02:00
Alibek Omarov e13a02e219 ChatActivity: fix crash, fix sticker button enabling conditions 2020-10-07 01:42:58 +03:00
Alibek Omarov 1805a99a6c ChatActivity: set sendbutton inactive when text or media are empty, fix media menu, fix media button stay inactive after removing media 2020-10-06 15:27:14 +03:00
Alibek Omarov 866e725d9c ComposeTokenizer: try to fix test 3 2020-10-05 23:31:17 +03:00
Alibek Omarov 7295ace378 ChatMessagesAdapter: fix non-clickable URLs 2020-10-05 23:16:46 +03:00
Alibek Omarov b5ffbfe000 ComposeTokenizer: try to fix test 2 2020-10-05 23:08:31 +03:00
Alibek Omarov e394eb6a5c ComposeTokenizer: try to fix test 2020-10-05 22:03:15 +03:00
Alibek Omarov ed2053b00f Account: add optional ap_id field 2020-10-05 21:15:30 +03:00
Alibek Omarov 5f198cde13 BottomSheetActivity: fix account URL search leading to wrong accounts or threads 2020-10-05 20:43:04 +03:00
Alibek Omarov 629a336e7e CommonComposeViewModel: filter all unrelated autocomplete suggestions 2020-10-05 19:51:56 +03:00
Alibek Omarov 3fff54c1f4 ComposeTokenizer: do not search stop after second @ 2020-10-05 19:51:17 +03:00
Alibek Omarov c5cedbe85f SpanUtils: allow extended username format 2020-10-05 19:50:31 +03:00
Alibek Omarov fabf921569 ChatActivity: fix #91 2020-10-05 17:59:40 +03:00
Alibek Omarov 9bdd4e49ca ComposeActivity: fix ignoring opened preview when back pressed 2020-10-05 17:49:57 +03:00
Alibek Omarov 7435d38d7b ChatsFragment: fix list not rendering sometimes 2020-10-05 17:43:56 +03:00
Alibek Omarov daabaf2bdb chats: implement attachment onClick 2020-10-05 17:25:49 +03:00
Alibek Omarov db18d55619 SFragment: open browser if attachment type is unknown 2020-10-05 17:24:49 +03:00
14 changed files with 219 additions and 71 deletions

View File

@ -34,7 +34,7 @@
<string name="notification_favourite_format">%s dodał Twój post do ulubionych</string>
<string name="action_hide_reblogs">Ukryj powtórzenia</string>
<string name="action_show_reblogs">Pokaż powtórzenia</string>
<string name="action_unreblog">Usuń powtórzenia</string>
<string name="action_unreblog">Usuń powtórzenie</string>
<string name="action_open_reblogged_by">Pokaż powtórzenia</string>
<string name="notification_boost_name">Powtórzenia</string>
<string name="action_open_reblogger">Otwórz konto osoby powtarzającej</string>
@ -61,4 +61,17 @@
<string name="error_sender_account_gone">Wysyłanie postu nie powiodło się.</string>
<string name="pref_title_alway_open_spoiler">Zawsze rozwijaj posty z ostrzeżeniami o zawartości</string>
<string name="action_access_scheduled_toot">Zaplanowane posty</string>
<string name="action_mark_as_read">Oznacz jako przeczytane</string>
<string name="action_open_in_external_app">Otwórz w zewnętrznej aplikacji</string>
<string name="notification_chat_message_format">%s wysłał(-a) Tobie wiadomość</string>
<string name="pref_title_privacy">Prywatność</string>
<string name="pref_summary_live_notifications">Może nieco zwiększyć zużycie energii</string>
<string name="attachment_type_image">Zdjęcie</string>
<string name="attachment_type_video">Wideo</string>
<string name="attachment_type_audio">Audio</string>
<string name="attachment_type_unknown">Załącznik</string>
<string name="link">Odnośnik</string>
<string name="status_replied_to_format">Odpowiedź dla %s</string>
<string name="chat_our_last_message"><b>Ty</b></string>
<string name="pref_title_other">Inne</string>
</resources>

View File

@ -83,12 +83,25 @@ abstract class BottomSheetActivity : BaseActivity() {
onEndSearch(url)
if (accounts.isNotEmpty()) {
// HACKHACK: Pleroma, remove when search will work normally
if (accounts[0].pleroma != null) {
val account = accounts.firstOrNull { it.pleroma?.apId == url || it.url == url }
if (account != null) {
viewAccount(account.id)
return@subscribe
}
} else {
viewAccount(accounts[0].id)
return@subscribe
}
}
if (statuses.isNotEmpty()) {
viewThread(statuses[0].id, statuses[0].url)
return@subscribe
} else if (accounts.isNotEmpty()) {
viewAccount(accounts[0].id)
return@subscribe
}
performUrlFallbackAction(url, lookupFallbackBehavior)

View File

@ -79,7 +79,16 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
return intent
}
fun newAvatarIntent(context: Context, url: String): Intent {
@JvmStatic
fun newIntent(context: Context?, attachment: Attachment): Intent {
val intent = Intent(context, ViewMediaActivity::class.java)
intent.putParcelableArrayListExtra(EXTRA_ATTACHMENTS,
arrayListOf(AttachmentViewData(attachment, null, null)))
intent.putExtra(EXTRA_ATTACHMENT_INDEX, 0)
return intent
}
fun newAvatarIntent(context: Context?, url: String): Intent {
val intent = Intent(context, ViewMediaActivity::class.java)
intent.putExtra(EXTRA_AVATAR_URL, url)
return intent
@ -171,6 +180,11 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
menu?.findItem(R.id.action_share_media)?.isEnabled = !isCreating
if(attachments != null) {
val isStatus = attachments!!.any { it.statusId != null && it.statusUrl != null }
menu?.findItem(R.id.action_open_status)?.isVisible = isStatus
}
return true
}

View File

@ -17,6 +17,7 @@ import com.keylesspalace.tusky.util.StatusDisplayOptions
import com.keylesspalace.tusky.util.ThemeUtils
import com.keylesspalace.tusky.util.TimestampUtils
import com.keylesspalace.tusky.util.emojify
import com.keylesspalace.tusky.util.LinkHelper
import com.keylesspalace.tusky.view.MediaPreviewImageView
import com.keylesspalace.tusky.viewdata.ChatMessageViewData
import java.text.SimpleDateFormat
@ -40,8 +41,10 @@ class ChatMessagesViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun setupWithChatMessage(msg: ChatMessageViewData.Concrete, chatActionListener: ChatActionListener, statusDisplayOptions: StatusDisplayOptions, payload: Any?) {
if(payload == null) {
if(msg.content != null)
content.text = msg.content.emojify(msg.emojis, content)
if(msg.content != null) {
val text = msg.content.emojify(msg.emojis, content)
LinkHelper.setClickableText(content, text, null, chatActionListener)
}
setAttachment(msg.attachment, chatActionListener)
setCreatedAt(msg.createdAt)

View File

@ -267,7 +267,7 @@ public class ComposeAutoCompleteAdapter extends BaseAdapter
}
public final static class AccountResult extends AutocompleteResult {
private final Account account;
public final Account account;
public AccountResult(Account account) {
this.account = account;

View File

@ -12,6 +12,7 @@ import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.KeyEvent
import android.view.MenuItem
import android.view.View
import android.widget.ImageButton
@ -20,10 +21,6 @@ import android.widget.Toast
import androidx.activity.viewModels
import androidx.annotation.StringRes
import androidx.annotation.VisibleForTesting
import com.keylesspalace.tusky.BottomSheetActivity
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.BuildConfig
import com.keylesspalace.tusky.ViewTagActivity
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.entity.Chat
@ -35,9 +32,11 @@ import com.keylesspalace.tusky.repository.ChatRepository
import com.keylesspalace.tusky.viewdata.ChatMessageViewData
import androidx.arch.core.util.Function
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.core.net.toUri
import androidx.core.view.ViewCompat
import androidx.core.view.inputmethod.InputConnectionCompat
import androidx.core.view.inputmethod.InputContentInfoCompat
import androidx.lifecycle.Lifecycle
@ -49,11 +48,13 @@ import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.snackbar.Snackbar
import com.keylesspalace.tusky.*
import com.keylesspalace.tusky.adapter.*
import com.keylesspalace.tusky.appstore.*
import com.keylesspalace.tusky.components.common.*
import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.components.compose.dialog.makeCaptionDialog
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.repository.Placeholder
import com.keylesspalace.tusky.repository.TimelineRequestMode
import com.keylesspalace.tusky.service.MessageToSend
@ -65,7 +66,6 @@ import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
import com.uber.autodispose.android.lifecycle.autoDispose
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
@ -208,7 +208,7 @@ class ChatActivity: BottomSheetActivity(),
subscribeToUpdates()
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
viewModel.tryFetchStickers = preferences.getBoolean("stickers", false)
viewModel.tryFetchStickers = preferences.getBoolean(PrefKeys.STICKERS, false)
viewModel.anonymizeNames = preferences.getBoolean(PrefKeys.ANONYMIZE_FILENAMES, false)
setupHeader()
@ -227,21 +227,23 @@ class ChatActivity: BottomSheetActivity(),
.subscribe { event: Event? ->
when(event) {
is ChatMessageDeliveredEvent -> {
onRefresh()
enableButton(sendButton, true, true)
enableButton(attachmentButton, true, true)
enableButton(stickerButton, haveStickers, haveStickers)
editText.text.clear()
viewModel.media.value = listOf()
}
if(event.chatMsg.chatId == chatId) {
onRefresh()
enableButton(attachmentButton, true, true)
enableButton(stickerButton, haveStickers, haveStickers)
sending = false
enableSendButton()
}
}
is ChatMessageReceivedEvent -> {
onRefresh()
if(event.chatMsg.chatId == chatId) {
onRefresh()
}
}
}
}
tryCache()
}
@ -287,11 +289,14 @@ class ChatActivity: BottomSheetActivity(),
viewModel.updateDescription(it.localId, newDescription)
}
}
removeId -> viewModel.removeMediaFromQueue(it)
removeId -> {
viewModel.removeMediaFromQueue(it)
}
}
}
true
}
popup.show()
}
imageAttachment.setOnClickListener(onMediaPick)
@ -314,6 +319,7 @@ class ChatActivity: BottomSheetActivity(),
highlightSpans(editText.text, mentionColour)
editText.afterTextChanged { editable ->
highlightSpans(editable, mentionColour)
enableSendButton()
}
// work around Android platform bug -> https://issuetracker.google.com/issues/67102093
@ -323,6 +329,17 @@ class ChatActivity: BottomSheetActivity(),
}
}
private var sending = false
private fun enableSendButton() {
if(sending)
return
val haveMedia = viewModel.media.value?.isNotEmpty() ?: false
val haveText = editText.text.isNotEmpty()
enableButton(sendButton, haveMedia || haveText, haveMedia || haveText)
}
override fun search(token: String): List<ComposeAutoCompleteAdapter.AutocompleteResult> {
return viewModel.searchAutocompleteSuggestions(token)
}
@ -354,11 +371,6 @@ class ChatActivity: BottomSheetActivity(),
viewModel.instanceParams.observe { instanceData ->
maximumTootCharacters = instanceData.chatLimit
}
viewModel.haveStickers.observe { haveStickers ->
if (haveStickers) {
stickerButton.visibility = View.VISIBLE
}
}
viewModel.instanceStickers.observe { stickers ->
if(stickers.isNotEmpty()) {
haveStickers = true
@ -369,7 +381,13 @@ class ChatActivity: BottomSheetActivity(),
}
viewModel.emoji.observe { setEmojiList(it) }
viewModel.media.observe {
if(it.isNotEmpty()) {
val notHaveMedia = it.isEmpty()
enableSendButton()
enableButton(attachmentButton, notHaveMedia, notHaveMedia)
enableButton(stickerButton, haveStickers && notHaveMedia, haveStickers && notHaveMedia)
if(!notHaveMedia) {
val media = it[0]
when(media.type) {
@ -461,22 +479,7 @@ class ChatActivity: BottomSheetActivity(),
emojiBehavior = BottomSheetBehavior.from(emojiView)
stickerBehavior = BottomSheetBehavior.from(stickerKeyboard)
sendButton.setOnClickListener {
val media = viewModel.getSingleMedia()
serviceClient.sendChatMessage( MessageToSend(
editText.text.toString(),
media?.id,
media?.uri?.toString(),
accountManager.activeAccount!!.id,
this.chatId,
0
))
enableButton(sendButton, false, false)
enableButton(attachmentButton, false, false)
enableButton(stickerButton, false, false)
}
sendButton.setOnClickListener { onSendClicked() }
attachmentButton.setOnClickListener { openPickDialog() }
emojiButton.setOnClickListener { showEmojis() }
@ -502,6 +505,26 @@ class ChatActivity: BottomSheetActivity(),
actionPhotoPick.setOnClickListener { onMediaPick() }
}
private fun onSendClicked() {
val media = viewModel.getSingleMedia()
serviceClient.sendChatMessage(MessageToSend(
editText.text.toString(),
media?.id,
media?.uri?.toString(),
accountManager.activeAccount!!.id,
this.chatId,
0
))
sending = true
editText.text.clear()
viewModel.media.value = listOf()
enableButton(sendButton, false, false)
enableButton(attachmentButton, false, false)
enableButton(stickerButton, false, false)
}
private fun openPickDialog() {
if (addMediaBehavior.state == BottomSheetBehavior.STATE_HIDDEN || addMediaBehavior.state == BottomSheetBehavior.STATE_COLLAPSED) {
addMediaBehavior.state = BottomSheetBehavior.STATE_EXPANDED
@ -652,8 +675,6 @@ class ChatActivity: BottomSheetActivity(),
}
}
displayTransientError(errorId)
} else {
enableButton(attachmentButton, false, false)
}
}
}
@ -862,13 +883,13 @@ class ChatActivity: BottomSheetActivity(),
FetchEnd.TOP -> {
updateMessages(msgs, fullFetch)
val pos = msgs.indexOfFirst { it.isRight() }
val last = msgs.indexOfFirst { it.isRight() }
mastodonApi.markChatAsRead(chatId, msgs[pos].asRight().id)
mastodonApi.markChatAsRead(chatId, msgs[last].asRight().id)
.observeOn(AndroidSchedulers.mainThread())
.autoDispose(this, Lifecycle.Event.ON_DESTROY)
.subscribe({
Log.d(TAG, "Marked new messages as read up to ${msgs[pos].asRight().id}")
Log.d(TAG, "Marked new messages as read up to ${msgs[last].asRight().id}")
}, {
Log.d(TAG, "Failed to mark messages as read", it)
})
@ -980,14 +1001,34 @@ class ChatActivity: BottomSheetActivity(),
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
Log.d(TAG, event.toString())
if(event.action == KeyEvent.ACTION_DOWN) {
if (event.isCtrlPressed) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {
// send message by pressing CTRL + ENTER
onSendClicked()
return true
}
}
if (keyCode == KeyEvent.KEYCODE_BACK) {
onBackPressed()
return true
}
}
return super.onKeyDown(keyCode, event)
}
override fun onBackPressed() {
// Acting like a teen: deliberately ignoring parent.
if (addMediaBehavior.state == BottomSheetBehavior.STATE_EXPANDED ||
emojiBehavior.state == BottomSheetBehavior.STATE_EXPANDED ||
stickerBehavior.state == BottomSheetBehavior.STATE_EXPANDED) {
if (addMediaBehavior.state != BottomSheetBehavior.STATE_HIDDEN ||
emojiBehavior.state != BottomSheetBehavior.STATE_HIDDEN ||
stickerBehavior.state != BottomSheetBehavior.STATE_HIDDEN) {
addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN
emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN
stickerBehavior.state = BottomSheetBehavior.STATE_HIDDEN
emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN
stickerBehavior.state = BottomSheetBehavior.STATE_HIDDEN
return
}
@ -1039,6 +1080,28 @@ class ChatActivity: BottomSheetActivity(),
startActivity(intent)
}
override fun onViewMedia(position: Int, view: View?) {
val attachment = msgs[position].asRight().attachment!!
when(attachment.type) {
Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.AUDIO, Attachment.Type.IMAGE -> {
val intent = ViewMediaActivity.newIntent(this, attachment)
if(view != null) {
val url = attachment.url
ViewCompat.setTransitionName(view, url)
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, view, url)
startActivity(intent, options.toBundle())
} else {
startActivity(intent)
}
}
Attachment.Type.UNKNOWN -> {
viewUrl(attachment.url)
}
}
}
companion object {
private const val MEDIA_PICK_RESULT = 1
private const val MEDIA_TAKE_PHOTO_RESULT = 2

View File

@ -252,9 +252,13 @@ open class CommonComposeViewModel(
when (token[0]) {
'@' -> {
return try {
api.searchAccounts(query = token.substring(1), limit = 10)
val acct = token.substring(1)
api.searchAccounts(query = acct, resolve = true, limit = 10)
.blockingGet()
.map { ComposeAutoCompleteAdapter.AccountResult(it) }
.filter {
it.account.username.startsWith(acct, ignoreCase = true)
}
} catch (e: Throwable) {
Log.e(TAG, String.format("Autocomplete search for %s failed.", token), e)
emptyList()

View File

@ -148,7 +148,7 @@ class ComposeActivity : BaseActivity(),
// do not do anything when not logged in, activity will be finished in super.onCreate() anyway
val activeAccount = accountManager.activeAccount ?: return
viewModel.tryFetchStickers = preferences.getBoolean("stickers", false)
viewModel.tryFetchStickers = preferences.getBoolean(PrefKeys.STICKERS, false)
viewModel.anonymizeNames = preferences.getBoolean(PrefKeys.ANONYMIZE_FILENAMES, false)
setupAvatar(preferences, activeAccount)
val mediaAdapter = MediaPreviewAdapter(
@ -1161,7 +1161,8 @@ class ComposeActivity : BaseActivity(),
addMediaBehavior.state == BottomSheetBehavior.STATE_EXPANDED ||
emojiBehavior.state == BottomSheetBehavior.STATE_EXPANDED ||
scheduleBehavior.state == BottomSheetBehavior.STATE_EXPANDED ||
stickerBehavior.state == BottomSheetBehavior.STATE_EXPANDED) {
stickerBehavior.state == BottomSheetBehavior.STATE_EXPANDED ||
previewBehavior.state == BottomSheetBehavior.STATE_HIDDEN) {
composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN
addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN
emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN

View File

@ -98,6 +98,7 @@ data class StringField (
)
data class PleromaAccount(
@SerializedName("ap_id") val apId: String? = null,
@SerializedName("accepts_chat_messages") val acceptsChatMessages: Boolean? = null,
@SerializedName("is_moderator") val isModerator: Boolean? = null,
@SerializedName("is_admin") val isAdmin: Boolean? = null

View File

@ -13,6 +13,7 @@ import androidx.lifecycle.Lifecycle
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.*
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import at.connyduck.sparkbutton.helpers.Utils
import com.keylesspalace.tusky.BottomSheetActivity
import com.keylesspalace.tusky.PostLookupFallbackBehavior
import com.keylesspalace.tusky.R
@ -95,8 +96,13 @@ class ChatsFragment : BaseFragment(), Injectable, RefreshableFragment, Reselecta
if (isAdded) {
Log.d(TAG, "onInserted");
adapter.notifyItemRangeInserted(position, count)
if (position == 0 && context != null) {
recyclerView.scrollToPosition(0)
// scroll up when new items at the top are loaded while being in the first position
// https://github.com/tuskyapp/Tusky/pull/1905#issuecomment-677819724
if (position == 0 && context != null && adapter.itemCount != count) {
if (isSwipeToRefreshEnabled)
recyclerView.scrollBy(0, Utils.dpToPx(context!!, -30));
else
recyclerView.scrollToPosition(0);
}
}
}

View File

@ -426,9 +426,7 @@ public abstract class SFragment extends BaseFragment implements Injectable {
break;
}
case UNKNOWN: {
/* Intentionally do nothing. This case is here is to handle when new attachment
* types are added to the API before code is added here to handle them. So, the
* best fallback is to just show the preview and ignore requests to view them. */
onViewUrl(active.getUrl());
break;
}
}

View File

@ -18,23 +18,55 @@ package com.keylesspalace.tusky.util
import android.text.SpannableString
import android.text.Spanned
import android.text.TextUtils
import android.util.Log
import android.widget.MultiAutoCompleteTextView
class ComposeTokenizer : MultiAutoCompleteTextView.Tokenizer {
private fun isMentionOrHashtagAllowedCharacter(character: Char) : Boolean {
return Character.isLetterOrDigit(character) || character == '_' // simple usernames
|| character == '-' // extended usernames
|| character == '.' // domain dot
}
override fun findTokenStart(text: CharSequence, cursor: Int): Int {
if (cursor == 0) {
return cursor
}
var i = cursor
var character = text[i - 1]
while (i > 0 && character != '@' && character != '#' && character != ':') {
// See SpanUtils.MENTION_REGEX
if (!Character.isLetterOrDigit(character) && character != '_') {
while(i > 0 && !(character == '@' || character == '#' || character == ':')) {
if(!isMentionOrHashtagAllowedCharacter(character)) {
return cursor
}
i--
character = if (i == 0) ' ' else text[i - 1]
}
// caught domain name, try search username
// don't ask me about this code
if(i > 3 && character == '@') {
var j = i - 1
var character2 = text[i - 2]
while(j > 0 && character2 != '@') {
if(!isMentionOrHashtagAllowedCharacter(character2)) {
break
}
j--
character2 = if (j == 0) ' ' else text[j - 1]
}
if(character2 == '@') {
i = j
character = character2
}
}
// Log.d("Tokenizer", "Stopped search at ${character} ${text.substring(i)}")
if (i < 1
|| (character != '@' && character != '#' && character != ':')
|| i > 1 && !Character.isWhitespace(text[i - 2])) {

View File

@ -18,7 +18,7 @@ private const val TAG_REGEX = "(?:^|[^/)A-Za-z0-9_])#([\\w_]*[\\p{Alpha}_][\\w_]
* @see <a href="https://github.com/tootsuite/mastodon/blob/master/app/models/account.rb">
* Account#MENTION_RE</a>
*/
private const val MENTION_REGEX = "(?:^|[^/[:word:]])@([a-z0-9_]+(?:@[a-z0-9\\.\\-]+[a-z0-9]+)?)"
private const val MENTION_REGEX = "(?:^|[^/[:word:]])@([a-z\\d_-]+(?:@[a-z0-9\\.\\-]+[a-z0-9]+)?)"
private const val HTTP_URL_REGEX = "(?:(^|\\b)http://[^\\s]+)"
private const val HTTPS_URL_REGEX = "(?:(^|\\b)https://[^\\s]+)"

View File

@ -8,8 +8,8 @@ import kotlinx.android.parcel.Parcelize
@Parcelize
data class AttachmentViewData(
val attachment: Attachment,
val statusId: String,
val statusUrl: String
val statusId: String?,
val statusUrl: String?
) : Parcelable {
companion object {
@JvmStatic
@ -27,4 +27,4 @@ data class AttachmentViewData(
}
}
}
}