fix user creation sequence. add resend confirm email dialog. (but api does not work)

This commit is contained in:
tateisu 2021-05-11 06:30:42 +09:00
parent 3f71b87125
commit 970264bd3a
9 changed files with 776 additions and 518 deletions

View File

@ -1826,8 +1826,8 @@ class ActMain : AsyncActivity(), View.OnClickListener,
var ta: TootAccount? = null var ta: TootAccount? = null
var sa: SavedAccount? = null var sa: SavedAccount? = null
var host: Host? = null var apiHost: Host? = null
var ti: TootInstance? = null var apDomain: Host? = null
override suspend fun background(client: TootApiClient): TootApiResult? { override suspend fun background(client: TootApiClient): TootApiResult? {
@ -1868,8 +1868,8 @@ class ActMain : AsyncActivity(), View.OnClickListener,
val (ti, r2) = TootInstance.get(client) val (ti, r2) = TootInstance.get(client)
ti ?: return r2 ti ?: return r2
this.ti = ti this.apiHost = instance
this.host = instance this.apDomain = ti.uri?.let{ Host.parse(it)}
val parser = TootParser( val parser = TootParser(
this@ActMain, this@ActMain,
@ -1938,7 +1938,8 @@ class ActMain : AsyncActivity(), View.OnClickListener,
val instance = client.apiHost val instance = client.apiHost
?: return TootApiResult("missing instance in callback url.") ?: return TootApiResult("missing instance in callback url.")
this.host = instance this.apiHost = instance
val parser = TootParser( val parser = TootParser(
@ -1955,24 +1956,26 @@ class ActMain : AsyncActivity(), View.OnClickListener,
this.ta = parser.account(it.jsonObject) this.ta = parser.account(it.jsonObject)
if( ta != null){ if( ta != null){
val (ti, ri) = TootInstance.getEx(client, forceAccessToken = refToken.get()) val (ti, ri) = TootInstance.getEx(client, forceAccessToken = refToken.get())
this.ti = ti ?: return ri ti ?: return ri
this.apDomain = ti.uri?.let{ it2-> Host.parse(it2)}
} }
} }
} }
} }
override suspend fun handleResult(result: TootApiResult?) { override suspend fun handleResult(result: TootApiResult?) {
val host = this.host val apiHost = this.apiHost
val apDomain = this.apDomain
val ta = this.ta val ta = this.ta
var sa = this.sa var sa = this.sa
if (ta != null && host?.isValid == true && sa == null) { if (ta != null && apiHost?.isValid == true && sa == null) {
val acct = Acct.parse(ta.username, host) val acct = Acct.parse(ta.username, apDomain ?: apiHost )
// アカウント追加時に、アプリ内に既にあるアカウントと同じものを登録していたかもしれない // アカウント追加時に、アプリ内に既にあるアカウントと同じものを登録していたかもしれない
sa = SavedAccount.loadAccountByAcct(this@ActMain, acct.ascii) sa = SavedAccount.loadAccountByAcct(this@ActMain, acct.ascii)
} }
afterAccountVerify(result, ta, sa, ti, host) afterAccountVerify(result, ta, sa, apiHost, apDomain)
} }
}) })
@ -1994,8 +1997,8 @@ class ActMain : AsyncActivity(), View.OnClickListener,
result: TootApiResult?, result: TootApiResult?,
ta: TootAccount?, ta: TootAccount?,
sa: SavedAccount?, sa: SavedAccount?,
ti: TootInstance?, apiHost: Host?,
host: Host? apDomain: Host?
): Boolean { ): Boolean {
result ?: return false result ?: return false
@ -2041,20 +2044,14 @@ class ActMain : AsyncActivity(), View.OnClickListener,
return true return true
} }
host != null -> { apiHost != null -> {
// アカウント追加時 // アカウント追加時
val user = Acct.parse(ta.username, host) val user = Acct.parse(ta.username, apDomain ?: apiHost)
val apDomain = ti?.uri
if (apDomain == null) {
showToast(false, "Can't get ActivityPub domain name.")
return false
}
val row_id = SavedAccount.insert( val row_id = SavedAccount.insert(
acct = user.ascii, acct = user.ascii,
host = host.ascii, host = apiHost.ascii,
domain = apDomain, domain = (apDomain ?: apiHost).ascii,
account = jsonObject, account = jsonObject,
token = token_info, token = token_info,
misskeyVersion = TootInstance.parseMisskeyVersion(token_info) misskeyVersion = TootInstance.parseMisskeyVersion(token_info)
@ -2120,12 +2117,17 @@ class ActMain : AsyncActivity(), View.OnClickListener,
TootTaskRunner(this@ActMain).run(apiHost, object : TootTask { TootTaskRunner(this@ActMain).run(apiHost, object : TootTask {
var ta: TootAccount? = null var ta: TootAccount? = null
var ti: TootInstance? = null var apDomain : Host? = null
override suspend fun background(client: TootApiClient): TootApiResult? { override suspend fun background(client: TootApiClient): TootApiResult? {
val (ti,ri) = TootInstance.getEx(client,forceAccessToken = access_token) val (ti,ri) = TootInstance.getEx(client,forceAccessToken = access_token)
this.ti = ti ?: return ri ti ?: return ri
val apDomain = ti.uri?.let { Host.parse(it) }
?: return TootApiResult("missing uri in Instance Information")
this.apDomain = apDomain
val misskeyVersion = ti.misskeyVersion val misskeyVersion = ti.misskeyVersion
@ -2134,8 +2136,8 @@ class ActMain : AsyncActivity(), View.OnClickListener,
this.ta = TootParser( this.ta = TootParser(
this@ActMain, this@ActMain,
LinkHelper.create( LinkHelper.create(
apiHost, apiHostArg = apiHost,
apDomainArg = ti.uri?.let { Host.parse(it) }, apDomainArg = apDomain,
misskeyVersion = misskeyVersion misskeyVersion = misskeyVersion
) )
).account(result?.jsonObject) ).account(result?.jsonObject)
@ -2144,7 +2146,7 @@ class ActMain : AsyncActivity(), View.OnClickListener,
} }
override suspend fun handleResult(result: TootApiResult?) { override suspend fun handleResult(result: TootApiResult?) {
if (afterAccountVerify(result, ta, sa, ti, apiHost)) { if (afterAccountVerify(result, ta, sa, apiHost, apDomain)) {
dialog_host?.dismissSafe() dialog_host?.dismissSafe()
dialog_token?.dismissSafe() dialog_token?.dismissSafe()
} }

View File

@ -26,6 +26,7 @@ import com.google.android.flexbox.JustifyContent
import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout
import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirection import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirection
import jp.juggler.emoji.UnicodeEmoji import jp.juggler.emoji.UnicodeEmoji
import jp.juggler.subwaytooter.action.Action_Account
import jp.juggler.subwaytooter.action.Action_List import jp.juggler.subwaytooter.action.Action_List
import jp.juggler.subwaytooter.action.Action_Notification import jp.juggler.subwaytooter.action.Action_Notification
import jp.juggler.subwaytooter.api.TootApiClient import jp.juggler.subwaytooter.api.TootApiClient
@ -87,7 +88,10 @@ class ColumnViewHolder(
private var status_adapter: ItemListAdapter? = null private var status_adapter: ItemListAdapter? = null
private var page_idx: Int = 0 private var page_idx: Int = 0
private lateinit var llLoading: View
private lateinit var btnConfirmMail: Button
private lateinit var tvLoading: TextView private lateinit var tvLoading: TextView
lateinit var listView: RecyclerView lateinit var listView: RecyclerView
lateinit var refreshLayout: SwipyRefreshLayout lateinit var refreshLayout: SwipyRefreshLayout
@ -297,7 +301,7 @@ class ColumnViewHolder(
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
private fun initLoadingTextView() { private fun initLoadingTextView() {
tvLoading.setOnTouchListener(ErrorFlickListener(activity)) llLoading.setOnTouchListener(ErrorFlickListener(activity))
} }
val viewRoot: View = inflate(activity, parent) val viewRoot: View = inflate(activity, parent)
@ -354,6 +358,7 @@ class ColumnViewHolder(
btnColumnClose.setOnClickListener(this) btnColumnClose.setOnClickListener(this)
btnColumnClose.setOnLongClickListener(this) btnColumnClose.setOnLongClickListener(this)
btnDeleteNotification.setOnClickListener(this) btnDeleteNotification.setOnClickListener(this)
btnConfirmMail.setOnClickListener(this)
btnColor.setOnClickListener(this) btnColor.setOnClickListener(this)
btnLanguageFilter.setOnClickListener(this) btnLanguageFilter.setOnClickListener(this)
@ -782,7 +787,13 @@ class ColumnViewHolder(
) )
} }
val streamStatus = column.getStreamingStatus() val streamStatus = column.getStreamingStatus()
log.d("procShowColumnStatus: streamStatus=${streamStatus}, column=${column.access_info.acct}/${column.getColumnName(true)}") log.d(
"procShowColumnStatus: streamStatus=${streamStatus}, column=${column.access_info.acct}/${
column.getColumnName(
true
)
}"
)
when (streamStatus) { when (streamStatus) {
StreamStatus.Missing, StreamStatus.Closed -> { StreamStatus.Missing, StreamStatus.Closed -> {
@ -1181,6 +1192,11 @@ class ColumnViewHolder(
activity.app_state.saveColumnList() activity.app_state.saveColumnList()
showAnnouncements() showAnnouncements()
} }
btnConfirmMail -> {
Action_Account.resendConfirmMail(activity, column.access_info)
}
} }
} }
@ -1198,12 +1214,13 @@ class ColumnViewHolder(
private fun showError(message: String) { private fun showError(message: String) {
hideRefreshError() hideRefreshError()
tvLoading.visibility = View.VISIBLE
tvLoading.text = message
refreshLayout.isRefreshing = false refreshLayout.isRefreshing = false
refreshLayout.visibility = View.GONE refreshLayout.visibility = View.GONE
llLoading.visibility = View.VISIBLE
tvLoading.text = message
btnConfirmMail.vg(column?.access_info?.isConfirmed == false)
} }
private fun showColumnCloseButton() { private fun showColumnCloseButton() {
@ -1300,7 +1317,7 @@ class ColumnViewHolder(
return return
} }
tvLoading.visibility = View.GONE llLoading.visibility = View.GONE
refreshLayout.visibility = View.VISIBLE refreshLayout.visibility = View.VISIBLE
@ -2353,9 +2370,27 @@ class ColumnViewHolder(
}.lparams(matchParent, matchParent) }.lparams(matchParent, matchParent)
llLoading = verticalLayout {
lparams(matchParent, matchParent)
isBaselineAligned = false
gravity = Gravity.CENTER
tvLoading = textView { tvLoading = textView {
gravity = Gravity.CENTER gravity = Gravity.CENTER
}.lparams(matchParent, matchParent) }.lparams(matchParent, wrapContent)
btnConfirmMail = button {
text = activity.getString(R.string.resend_confirm_mail)
background = ContextCompat.getDrawable(
activity,
R.drawable.btn_bg_transparent_round6dp
)
}.lparams(matchParent, wrapContent) {
topMargin = dip(8)
}
}
refreshLayout = swipyRefreshLayout { refreshLayout = swipyRefreshLayout {
lparams(matchParent, matchParent) lparams(matchParent, matchParent)
@ -2744,7 +2779,7 @@ class ColumnViewHolder(
if (sample == null) { if (sample == null) {
EmojiPicker(activity, column.access_info, closeOnSelected = true) { result -> EmojiPicker(activity, column.access_info, closeOnSelected = true) { result ->
val emoji = result.emoji val emoji = result.emoji
val code = when(emoji){ val code = when (emoji) {
is UnicodeEmoji -> emoji.unifiedCode is UnicodeEmoji -> emoji.unifiedCode
is CustomEmoji -> emoji.shortcode is CustomEmoji -> emoji.shortcode
else -> error("unknown emoji type") else -> error("unknown emoji type")

View File

@ -3,13 +3,11 @@ package jp.juggler.subwaytooter.action
import android.app.Dialog import android.app.Dialog
import android.os.Build import android.os.Build
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import jp.juggler.subwaytooter.* import jp.juggler.subwaytooter.*
import jp.juggler.subwaytooter.api.* import jp.juggler.subwaytooter.api.*
import jp.juggler.subwaytooter.api.entity.* import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.dialog.AccountPicker import jp.juggler.subwaytooter.dialog.*
import jp.juggler.subwaytooter.dialog.DlgCreateAccount
import jp.juggler.subwaytooter.dialog.DlgTextInput
import jp.juggler.subwaytooter.dialog.LoginForm
import jp.juggler.subwaytooter.table.SavedAccount import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.table.UserRelation import jp.juggler.subwaytooter.table.UserRelation
import jp.juggler.subwaytooter.util.LinkHelper import jp.juggler.subwaytooter.util.LinkHelper
@ -22,7 +20,7 @@ object Action_Account {
private val log = LogCategory("Action_Account") private val log = LogCategory("Action_Account")
// アカウントの追加 // アカウントの追加
fun add(activity : ActMain) { fun add(activity: ActMain) {
LoginForm.showLoginForm( LoginForm.showLoginForm(
activity, activity,
@ -30,8 +28,8 @@ object Action_Account {
) { dialog, instance, action -> ) { dialog, instance, action ->
TootTaskRunner(activity).run(instance, object : TootTask { TootTaskRunner(activity).run(instance, object : TootTask {
override suspend fun background(client : TootApiClient) : TootApiResult? { override suspend fun background(client: TootApiClient): TootApiResult? {
return when(action) { return when (action) {
LoginForm.Action.Existing -> LoginForm.Action.Existing ->
client.authentication1(Pref.spClientName(activity)) client.authentication1(Pref.spClientName(activity))
@ -42,27 +40,27 @@ object Action_Account {
LoginForm.Action.Pseudo, LoginForm.Action.Pseudo,
LoginForm.Action.Token -> { LoginForm.Action.Token -> {
val (ti, ri) = TootInstance.get(client) val (ti, ri) = TootInstance.get(client)
if(ti != null) ri?.data = ti if (ti != null) ri?.data = ti
ri ri
} }
} }
} }
override suspend fun handleResult(result : TootApiResult?) { override suspend fun handleResult(result: TootApiResult?) {
result ?: return // cancelled. result ?: return // cancelled.
val data = result.data val data = result.data
if(result.error == null && data != null) { if (result.error == null && data != null) {
when(action) { when (action) {
LoginForm.Action.Existing -> if(data is String) { LoginForm.Action.Existing -> if (data is String) {
// ブラウザ用URLが生成された // ブラウザ用URLが生成された
activity.openBrowser(data.toUri()) activity.openBrowser(data.toUri())
dialog.dismissSafe() dialog.dismissSafe()
return return
} }
LoginForm.Action.Create -> if(data is JsonObject) { LoginForm.Action.Create -> if (data is JsonObject) {
// インスタンスを確認できた // インスタンスを確認できた
createAccount( createAccount(
activity, activity,
@ -73,7 +71,7 @@ object Action_Account {
return return
} }
LoginForm.Action.Pseudo -> if(data is TootInstance) { LoginForm.Action.Pseudo -> if (data is TootInstance) {
addPseudoAccount( addPseudoAccount(
activity, activity,
instance, instance,
@ -86,7 +84,7 @@ object Action_Account {
} }
} }
LoginForm.Action.Token -> if(data is TootInstance) { LoginForm.Action.Token -> if (data is TootInstance) {
DlgTextInput.show( DlgTextInput.show(
activity, activity,
activity.getString(R.string.access_token_or_api_token), activity.getString(R.string.access_token_or_api_token),
@ -95,8 +93,8 @@ object Action_Account {
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
override fun onOK( override fun onOK(
dialog_token : Dialog, dialog_token: Dialog,
text : String text: String
) { ) {
// dialog引数が二つあるのに注意 // dialog引数が二つあるのに注意
@ -120,10 +118,10 @@ object Action_Account {
} }
val errorText = result.error ?: "(no error information)" val errorText = result.error ?: "(no error information)"
if(errorText.contains("SSLHandshakeException") if (errorText.contains("SSLHandshakeException")
&& (Build.VERSION.RELEASE.startsWith("7.0") && (Build.VERSION.RELEASE.startsWith("7.0")
|| Build.VERSION.RELEASE.startsWith("7.1") || Build.VERSION.RELEASE.startsWith("7.1")
&& ! Build.VERSION.RELEASE.startsWith("7.1.") && !Build.VERSION.RELEASE.startsWith("7.1.")
) )
) { ) {
AlertDialog.Builder(activity) AlertDialog.Builder(activity)
@ -139,22 +137,22 @@ object Action_Account {
} }
private fun createAccount( private fun createAccount(
activity : ActMain, activity: ActMain,
instance : Host, apiHost: Host,
client_info : JsonObject, client_info: JsonObject,
dialog_host : Dialog dialog_host: Dialog
) { ) {
DlgCreateAccount( DlgCreateAccount(
activity, activity,
instance apiHost
) { dialog_create, username, email, password, agreement, reason -> ) { dialog_create, username, email, password, agreement, reason ->
// dialog引数が二つあるのに注意 // dialog引数が二つあるのに注意
TootTaskRunner(activity).run(instance, object : TootTask { TootTaskRunner(activity).run(apiHost, object : TootTask {
var ta : TootAccount? = null var ta: TootAccount? = null
var ti : TootInstance? = null var apDomain: Host? = null
override suspend fun background(client : TootApiClient) : TootApiResult? { override suspend fun background(client: TootApiClient): TootApiResult? {
val r1 = client.createUser2Mastodon( val r1 = client.createUser2Mastodon(
client_info, client_info,
username, username,
@ -163,41 +161,42 @@ object Action_Account {
agreement, agreement,
reason reason
) )
val ti = r1?.jsonObject ?: return r1 val tokenJson = r1?.jsonObject ?: return r1
val misskeyVersion = TootInstance.parseMisskeyVersion(tokenJson)
val misskeyVersion = TootInstance.parseMisskeyVersion(ti)
val parser = TootParser( val parser = TootParser(
activity, activity,
linkHelper = LinkHelper.create(instance, misskeyVersion = misskeyVersion) linkHelper = LinkHelper.create(apiHost, misskeyVersion = misskeyVersion)
) )
this.ti = TootInstance(parser, ti) // ここだけMastodon専用
val access_token = tokenJson.string("access_token")
val access_token = ti.string("access_token")
?: return TootApiResult("can't get user access token") ?: return TootApiResult("can't get user access token")
client.apiHost = apiHost
val (ti, ri) = TootInstance.getEx(client, forceAccessToken = access_token)
ti ?: return ri
this.apDomain = ti.uri?.let { Host.parse(it) }
val r2 = client.getUserCredential(access_token, misskeyVersion = misskeyVersion) val r2 = client.getUserCredential(access_token, misskeyVersion = misskeyVersion)
this.ta = parser.account(r2?.jsonObject) this.ta = parser.account(r2?.jsonObject)
if(this.ta != null) return r2 if (this.ta != null) return r2
val jsonObject = jsonObject { val jsonObject = jsonObject {
put("id", EntityId.CONFIRMING.toString()) put("id", EntityId.CONFIRMING.toString())
put("username", username) put("username", username)
put("acct", username) put("acct", username)
put("acct", username) put("url", "https://$apiHost/@$username")
put("url", "https://$instance/@$username")
} }
this.ta = parser.account(jsonObject) this.ta = parser.account(jsonObject)
r1.data = jsonObject r1.data = jsonObject
r1.tokenInfo = ti r1.tokenInfo = tokenJson
return r1 return r1
} }
override suspend fun handleResult(result : TootApiResult?) { override suspend fun handleResult(result: TootApiResult?) {
val sa : SavedAccount? = null val sa: SavedAccount? = null
if(activity.afterAccountVerify(result, ta, sa, ti, instance)) { if (activity.afterAccountVerify(result, ta, sa, apiHost, apDomain)) {
dialog_host.dismissSafe() dialog_host.dismissSafe()
dialog_create.dismissSafe() dialog_create.dismissSafe()
} }
@ -207,7 +206,7 @@ object Action_Account {
} }
// アカウント設定 // アカウント設定
fun setting(activity : ActMain) { fun setting(activity: ActMain) {
AccountPicker.pick( AccountPicker.pick(
activity, activity,
bAllowPseudo = true, bAllowPseudo = true,
@ -218,10 +217,10 @@ object Action_Account {
// アカウントを選んでタイムラインカラムを追加 // アカウントを選んでタイムラインカラムを追加
fun timeline( fun timeline(
activity : ActMain, activity: ActMain,
pos : Int, pos: Int,
type : ColumnType, type: ColumnType,
args : Array<out Any> = emptyArray() args: Array<out Any> = emptyArray()
) { ) {
AccountPicker.pick( AccountPicker.pick(
@ -235,11 +234,11 @@ object Action_Account {
type.name1(activity) type.name1(activity)
) )
) { ai -> ) { ai ->
when(type) { when (type) {
ColumnType.PROFILE -> { ColumnType.PROFILE -> {
val id = ai.loginAccount?.id val id = ai.loginAccount?.id
if(id != null) activity.addColumn(pos, ai, type, id) if (id != null) activity.addColumn(pos, ai, type, id)
} }
ColumnType.PROFILE_DIRECTORY -> ColumnType.PROFILE_DIRECTORY ->
@ -252,13 +251,13 @@ object Action_Account {
// 投稿画面を開く。初期テキストを指定する // 投稿画面を開く。初期テキストを指定する
fun openPost( fun openPost(
activity : ActMain, activity: ActMain,
initial_text : String? = activity.quickTootText initial_text: String? = activity.quickTootText
) { ) {
activity.post_helper.closeAcctPopup() activity.post_helper.closeAcctPopup()
val db_id = activity.currentPostTarget?.db_id ?: - 1L val db_id = activity.currentPostTarget?.db_id ?: -1L
if(db_id != - 1L) { if (db_id != -1L) {
ActPost.open(activity, ActMain.REQUEST_CODE_POST, db_id, initial_text = initial_text) ActPost.open(activity, ActMain.REQUEST_CODE_POST, db_id, initial_text = initial_text)
} else { } else {
AccountPicker.pick( AccountPicker.pick(
@ -278,12 +277,12 @@ object Action_Account {
} }
fun endorse( fun endorse(
activity : ActMain, activity: ActMain,
access_info : SavedAccount, access_info: SavedAccount,
who : TootAccount, who: TootAccount,
bSet : Boolean bSet: Boolean
) { ) {
if(access_info.isMisskey) { if (access_info.isMisskey) {
activity.showToast(false, "This feature is not provided on Misskey account.") activity.showToast(false, "This feature is not provided on Misskey account.")
return return
} }
@ -291,38 +290,38 @@ object Action_Account {
TootTaskRunner(activity).run(access_info, object : TootTask { TootTaskRunner(activity).run(access_info, object : TootTask {
var relation : UserRelation? = null var relation: UserRelation? = null
override suspend fun background(client : TootApiClient) : TootApiResult? { override suspend fun background(client: TootApiClient): TootApiResult? {
val result = client.request( val result = client.request(
"/api/v1/accounts/${who.id}/" + when(bSet) { "/api/v1/accounts/${who.id}/" + when (bSet) {
true -> "pin" true -> "pin"
false -> "unpin" false -> "unpin"
}, },
"".toFormRequestBody().toPost() "".toFormRequestBody().toPost()
) )
val jsonObject = result?.jsonObject val jsonObject = result?.jsonObject
if(jsonObject != null) { if (jsonObject != null) {
val tr = parseItem( val tr = parseItem(
::TootRelationShip, ::TootRelationShip,
TootParser(client.context, access_info), TootParser(client.context, access_info),
jsonObject jsonObject
) )
if(tr != null) { if (tr != null) {
this.relation = saveUserRelation(access_info, tr) this.relation = saveUserRelation(access_info, tr)
} }
} }
return result return result
} }
override suspend fun handleResult(result : TootApiResult?) { override suspend fun handleResult(result: TootApiResult?) {
result ?: return result ?: return
if(result.error != null) { if (result.error != null) {
activity.showToast(true, result.error) activity.showToast(true, result.error)
} else { } else {
activity.showToast( activity.showToast(
false, when(bSet) { false, when (bSet) {
true -> R.string.endorse_succeeded true -> R.string.endorse_succeeded
else -> R.string.remove_endorse_succeeded else -> R.string.remove_endorse_succeeded
} }
@ -331,4 +330,42 @@ object Action_Account {
} }
}) })
} }
private val mailRegex =
"""\A[a-z0-9_+&*-]+(?:\.[a-z0-9_+&*-]+)*@(?:[a-z0-9-]+\.)+[a-z]{2,12}\z""".toRegex(
RegexOption.IGNORE_CASE
)
fun resendConfirmMail(activity: AppCompatActivity, accessInfo: SavedAccount) {
DlgConfirmMail(
activity,
accessInfo
) { email ->
TootTaskRunner(activity).run(accessInfo, object : TootTask {
override suspend fun background(client: TootApiClient): TootApiResult? {
email?.let {
if (!mailRegex.matches(it))
return TootApiResult("email address is not valid.")
}
return client.request(
"/api/v1/emails/confirmations",
ArrayList<String>().apply {
if (email != null) add("email=${email.encodePercent()}")
}.joinToString("&").toFormRequestBody().toPost()
)
}
override suspend fun handleResult(result: TootApiResult?) {
result ?: return // cancelled.
when (val error = result.error) {
null ->
activity.showToast(true, R.string.resend_confirm_mail_requested)
else ->
activity.showToast(true, error)
}
}
})
}.show()
}
} }

View File

@ -475,7 +475,7 @@ class TootInstance(parser: TootParser, src: JsonObject) {
TootParser( TootParser(
client.context, client.context,
linkHelper = linkHelper ?: LinkHelper.create( linkHelper = linkHelper ?: LinkHelper.create(
hostArg!!, (hostArg ?: client.apiHost)!!,
misskeyVersion = parseMisskeyVersion(json) misskeyVersion = parseMisskeyVersion(json)
) )
), ),

View File

@ -0,0 +1,73 @@
package jp.juggler.subwaytooter.dialog
import android.annotation.SuppressLint
import android.app.Dialog
import android.view.View
import android.view.WindowManager
import android.widget.Button
import android.widget.CheckBox
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.table.SavedAccount
class DlgConfirmMail(
val activity: AppCompatActivity,
val accessInfo: SavedAccount,
val onClickOk: (email: String?) -> Unit
) : View.OnClickListener {
@SuppressLint("InflateParams")
private val viewRoot = activity.layoutInflater
.inflate(R.layout.dlg_confirm_mail, null, false)
private val cbUpdateMailAddress: CheckBox = viewRoot.findViewById(R.id.cbUpdateMailAddress)
private val etEmail: EditText = viewRoot.findViewById(R.id.etEmail)
private val dialog = Dialog(activity)
init {
viewRoot.findViewById<TextView>(R.id.tvUserName).text = accessInfo.acct.pretty
viewRoot.findViewById<TextView>(R.id.tvInstance).text =
if (accessInfo.apiHost != accessInfo.apDomain) {
"${accessInfo.apiHost.pretty} (${accessInfo.apDomain.pretty})"
} else {
accessInfo.apiHost.pretty
}
cbUpdateMailAddress.setOnCheckedChangeListener { _, isChecked ->
etEmail.isEnabled = isChecked
}
arrayOf(
R.id.btnCancel,
R.id.btnOk
).forEach {
viewRoot.findViewById<Button>(it)?.setOnClickListener(this)
}
}
fun show() {
dialog.setContentView(viewRoot)
dialog.window?.setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT
)
dialog.show()
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnCancel ->
dialog.cancel()
R.id.btnOk ->
onClickOk(
if (cbUpdateMailAddress.isChecked) etEmail.text.toString().trim() else null
)
}
}
}

View File

@ -1,12 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
tools:ignore="UnusedAttribute" tools:ignore="UnusedAttribute">
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:ignore="UnusedAttribute">
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -14,16 +18,15 @@
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:text="@string/instance" android:text="@string/instance" />
/>
<TextView <TextView
android:id="@+id/tvInstance" android:id="@+id/tvInstance"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp" />
/>
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -31,16 +34,14 @@
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:text="@string/description" android:text="@string/description" />
/>
<TextView <TextView
android:id="@+id/tvDescription" android:id="@+id/tvDescription"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp" />
/>
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -48,8 +49,7 @@
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:text="@string/user_name" android:text="@string/user_name" />
/>
<EditText <EditText
android:id="@+id/etUserName" android:id="@+id/etUserName"
@ -58,18 +58,18 @@
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:hint="@string/user_name_hint" android:hint="@string/user_name_hint"
android:inputType="text"
android:importantForAutofill="no" android:importantForAutofill="no"
tools:ignore="UnusedAttribute" android:inputType="text"
/> tools:ignore="UnusedAttribute" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:text="@string/email" android:text="@string/email" />
/>
<EditText <EditText
android:id="@+id/etEmail" android:id="@+id/etEmail"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -77,10 +77,9 @@
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:hint="@string/email_hint" android:hint="@string/email_hint"
android:inputType="text"
android:importantForAutofill="no" android:importantForAutofill="no"
tools:ignore="TextFields" android:inputType="text"
/> tools:ignore="TextFields" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -88,8 +87,7 @@
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:text="@string/password" android:text="@string/password" />
/>
<EditText <EditText
android:id="@+id/etPassword" android:id="@+id/etPassword"
@ -97,21 +95,19 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:inputType="textPassword"
android:importantForAutofill="no"
android:hint="@string/password_hint" android:hint="@string/password_hint"
/> android:importantForAutofill="no"
android:inputType="textPassword" />
<TextView <TextView
android:id="@+id/tvReasonCaption"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:text="@string/reason_create_account"
android:id="@+id/tvReasonCaption"
android:labelFor="@+id/etReason" android:labelFor="@+id/etReason"
/> android:text="@string/reason_create_account" />
<EditText <EditText
android:id="@+id/etReason" android:id="@+id/etReason"
@ -119,21 +115,20 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:inputType="text"
android:importantForAutofill="no" android:importantForAutofill="no"
/> android:inputType="text" />
<Button <Button
android:id="@+id/btnRules" android:id="@+id/btnRules"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:text="@string/instance_rules" android:text="@string/instance_rules"
android:textAllCaps="false" android:textAllCaps="false" />
/>
<Button <Button
android:id="@+id/btnTerms" android:id="@+id/btnTerms"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -141,8 +136,7 @@
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:text="@string/privacy_policy" android:text="@string/privacy_policy"
android:textAllCaps="false" android:textAllCaps="false" />
/>
<CheckBox <CheckBox
android:id="@+id/cbAgreement" android:id="@+id/cbAgreement"
@ -150,14 +144,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:text="@string/agree_terms" android:text="@string/agree_terms" />
/>
<LinearLayout <LinearLayout
style="?android:attr/buttonBarStyle" style="?android:attr/buttonBarStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal">
>
<Button <Button
android:id="@+id/btnCancel" android:id="@+id/btnCancel"
@ -165,8 +158,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/cancel" android:text="@string/cancel" />
/>
<Button <Button
android:id="@+id/btnOk" android:id="@+id/btnOk"
@ -174,7 +166,9 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/ok" android:text="@string/ok" />
/>
</LinearLayout> </LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:ignore="UnusedAttribute">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:ignore="UnusedAttribute">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="12dp"
android:text="@string/instance" />
<TextView
android:id="@+id/tvInstance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="12dp"
android:text="@string/user_name" />
<TextView
android:id="@+id/tvUserName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:hint="@string/user_name_hint"
android:importantForAutofill="no"
tools:ignore="UnusedAttribute" />
<CheckBox
android:id="@+id/cbUpdateMailAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="12dp"
android:text="@string/update_mail_address" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="12dp"
android:text="@string/email" />
<EditText
android:id="@+id/etEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:enabled="false"
android:hint="@string/email_hint"
android:importantForAutofill="no"
android:inputType="text"
tools:ignore="TextFields" />
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnCancel"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/cancel" />
<Button
android:id="@+id/btnOk"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/ok" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="12dp"
android:text="@string/confirm_mail_description" />
</LinearLayout>
</ScrollView>

View File

@ -1073,5 +1073,9 @@
<string name="server_has_no_support_of_visibility">公開範囲\'%1$s\'はこのサーバで使えません</string> <string name="server_has_no_support_of_visibility">公開範囲\'%1$s\'はこのサーバで使えません</string>
<string name="in_app_unicode_emoji">アプリ内蔵のUnicode絵文字を使う(アプリ再起動が必要)</string> <string name="in_app_unicode_emoji">アプリ内蔵のUnicode絵文字を使う(アプリ再起動が必要)</string>
<string name="emoji_category_composite_tones">複合トーン</string> <string name="emoji_category_composite_tones">複合トーン</string>
<string name="resend_confirm_mail">確認メールの再送…</string>
<string name="update_mail_address">登録メールアドレスを変更する</string>
<string name="resend_confirm_mail_requested">確認メールの再送を要求しました。</string>
<string name="confirm_mail_description">確認メールの再送を要求した後の手順:\n- あなたのメーラーで新着メールが届くのを確認する。\n- メール中の確認リンクを開く。\n- このダイアログを閉じてカラムをリロードする。</string>
</resources> </resources>

View File

@ -1087,4 +1087,8 @@
<string name="show_emoji_picker_other_category">Show emoji picker other category</string> <string name="show_emoji_picker_other_category">Show emoji picker other category</string>
<string name="in_app_unicode_emoji">use in-app Unicode Emoji (app restart required)</string> <string name="in_app_unicode_emoji">use in-app Unicode Emoji (app restart required)</string>
<string name="emoji_category_composite_tones">Composite Tones</string> <string name="emoji_category_composite_tones">Composite Tones</string>
<string name="resend_confirm_mail">Resend confirm mail…</string>
<string name="update_mail_address">Update e-mail address</string>
<string name="resend_confirm_mail_requested">Resending confirm E-mail was requested.</string>
<string name="confirm_mail_description">After requesting resending confirm E-mail,\n- please check the mail on your mailer.\n- open confirm link in the mail.\n- close this dialog and reload column.</string>
</resources> </resources>