From 970264bd3af4d68fd37551461298e4b4fe4f9dcc Mon Sep 17 00:00:00 2001 From: tateisu Date: Tue, 11 May 2021 06:30:42 +0900 Subject: [PATCH] fix user creation sequence. add resend confirm email dialog. (but api does not work) --- .../java/jp/juggler/subwaytooter/ActMain.kt | 56 +- .../juggler/subwaytooter/ColumnViewHolder.kt | 51 +- .../subwaytooter/action/Action_Account.kt | 673 +++++++++--------- .../subwaytooter/api/entity/TootInstance.kt | 2 +- .../subwaytooter/dialog/DlgConfirmMail.kt | 73 ++ .../main/res/layout/dlg_account_create.xml | 322 ++++----- app/src/main/res/layout/dlg_confirm_mail.xml | 109 +++ app/src/main/res/values-ja/strings.xml | 4 + app/src/main/res/values/strings.xml | 4 + 9 files changed, 776 insertions(+), 518 deletions(-) create mode 100644 app/src/main/java/jp/juggler/subwaytooter/dialog/DlgConfirmMail.kt create mode 100644 app/src/main/res/layout/dlg_confirm_mail.xml diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt b/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt index 91f9f2f7..0b0a23f4 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ActMain.kt @@ -1826,8 +1826,8 @@ class ActMain : AsyncActivity(), View.OnClickListener, var ta: TootAccount? = null var sa: SavedAccount? = null - var host: Host? = null - var ti: TootInstance? = null + var apiHost: Host? = null + var apDomain: Host? = null override suspend fun background(client: TootApiClient): TootApiResult? { @@ -1868,8 +1868,8 @@ class ActMain : AsyncActivity(), View.OnClickListener, val (ti, r2) = TootInstance.get(client) ti ?: return r2 - this.ti = ti - this.host = instance + this.apiHost = instance + this.apDomain = ti.uri?.let{ Host.parse(it)} val parser = TootParser( this@ActMain, @@ -1938,7 +1938,8 @@ class ActMain : AsyncActivity(), View.OnClickListener, val instance = client.apiHost ?: return TootApiResult("missing instance in callback url.") - this.host = instance + this.apiHost = instance + val parser = TootParser( @@ -1955,24 +1956,26 @@ class ActMain : AsyncActivity(), View.OnClickListener, this.ta = parser.account(it.jsonObject) if( ta != null){ 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?) { - val host = this.host + val apiHost = this.apiHost + val apDomain = this.apDomain val ta = this.ta var sa = this.sa - if (ta != null && host?.isValid == true && sa == null) { - val acct = Acct.parse(ta.username, host) + if (ta != null && apiHost?.isValid == true && sa == null) { + val acct = Acct.parse(ta.username, apDomain ?: apiHost ) // アカウント追加時に、アプリ内に既にあるアカウントと同じものを登録していたかもしれない 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?, ta: TootAccount?, sa: SavedAccount?, - ti: TootInstance?, - host: Host? + apiHost: Host?, + apDomain: Host? ): Boolean { result ?: return false @@ -2041,20 +2044,14 @@ class ActMain : AsyncActivity(), View.OnClickListener, return true } - host != null -> { + apiHost != null -> { // アカウント追加時 - val user = Acct.parse(ta.username, host) - - val apDomain = ti?.uri - if (apDomain == null) { - showToast(false, "Can't get ActivityPub domain name.") - return false - } + val user = Acct.parse(ta.username, apDomain ?: apiHost) val row_id = SavedAccount.insert( acct = user.ascii, - host = host.ascii, - domain = apDomain, + host = apiHost.ascii, + domain = (apDomain ?: apiHost).ascii, account = jsonObject, token = token_info, misskeyVersion = TootInstance.parseMisskeyVersion(token_info) @@ -2120,12 +2117,17 @@ class ActMain : AsyncActivity(), View.OnClickListener, TootTaskRunner(this@ActMain).run(apiHost, object : TootTask { var ta: TootAccount? = null - var ti: TootInstance? = null + var apDomain : Host? = null override suspend fun background(client: TootApiClient): TootApiResult? { 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 @@ -2134,8 +2136,8 @@ class ActMain : AsyncActivity(), View.OnClickListener, this.ta = TootParser( this@ActMain, LinkHelper.create( - apiHost, - apDomainArg = ti.uri?.let { Host.parse(it) }, + apiHostArg = apiHost, + apDomainArg = apDomain, misskeyVersion = misskeyVersion ) ).account(result?.jsonObject) @@ -2144,7 +2146,7 @@ class ActMain : AsyncActivity(), View.OnClickListener, } override suspend fun handleResult(result: TootApiResult?) { - if (afterAccountVerify(result, ta, sa, ti, apiHost)) { + if (afterAccountVerify(result, ta, sa, apiHost, apDomain)) { dialog_host?.dismissSafe() dialog_token?.dismissSafe() } diff --git a/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.kt b/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.kt index 8130ef46..849481d0 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.kt @@ -26,6 +26,7 @@ import com.google.android.flexbox.JustifyContent import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout import com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayoutDirection 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_Notification import jp.juggler.subwaytooter.api.TootApiClient @@ -87,7 +88,10 @@ class ColumnViewHolder( private var status_adapter: ItemListAdapter? = null private var page_idx: Int = 0 + private lateinit var llLoading: View + private lateinit var btnConfirmMail: Button private lateinit var tvLoading: TextView + lateinit var listView: RecyclerView lateinit var refreshLayout: SwipyRefreshLayout @@ -297,7 +301,7 @@ class ColumnViewHolder( @SuppressLint("ClickableViewAccessibility") private fun initLoadingTextView() { - tvLoading.setOnTouchListener(ErrorFlickListener(activity)) + llLoading.setOnTouchListener(ErrorFlickListener(activity)) } val viewRoot: View = inflate(activity, parent) @@ -354,6 +358,7 @@ class ColumnViewHolder( btnColumnClose.setOnClickListener(this) btnColumnClose.setOnLongClickListener(this) btnDeleteNotification.setOnClickListener(this) + btnConfirmMail.setOnClickListener(this) btnColor.setOnClickListener(this) btnLanguageFilter.setOnClickListener(this) @@ -782,7 +787,13 @@ class ColumnViewHolder( ) } 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) { StreamStatus.Missing, StreamStatus.Closed -> { @@ -1181,6 +1192,11 @@ class ColumnViewHolder( activity.app_state.saveColumnList() showAnnouncements() } + + btnConfirmMail -> { + Action_Account.resendConfirmMail(activity, column.access_info) + + } } } @@ -1198,12 +1214,13 @@ class ColumnViewHolder( private fun showError(message: String) { hideRefreshError() - tvLoading.visibility = View.VISIBLE - tvLoading.text = message refreshLayout.isRefreshing = false refreshLayout.visibility = View.GONE + llLoading.visibility = View.VISIBLE + tvLoading.text = message + btnConfirmMail.vg(column?.access_info?.isConfirmed == false) } private fun showColumnCloseButton() { @@ -1300,7 +1317,7 @@ class ColumnViewHolder( return } - tvLoading.visibility = View.GONE + llLoading.visibility = View.GONE refreshLayout.visibility = View.VISIBLE @@ -2353,9 +2370,27 @@ class ColumnViewHolder( }.lparams(matchParent, matchParent) - tvLoading = textView { + llLoading = verticalLayout { + lparams(matchParent, matchParent) + + isBaselineAligned = false + gravity = Gravity.CENTER - }.lparams(matchParent, matchParent) + + tvLoading = textView { + gravity = Gravity.CENTER + }.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 { lparams(matchParent, matchParent) @@ -2744,7 +2779,7 @@ class ColumnViewHolder( if (sample == null) { EmojiPicker(activity, column.access_info, closeOnSelected = true) { result -> val emoji = result.emoji - val code = when(emoji){ + val code = when (emoji) { is UnicodeEmoji -> emoji.unifiedCode is CustomEmoji -> emoji.shortcode else -> error("unknown emoji type") diff --git a/app/src/main/java/jp/juggler/subwaytooter/action/Action_Account.kt b/app/src/main/java/jp/juggler/subwaytooter/action/Action_Account.kt index 528d6127..dbd46c78 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/action/Action_Account.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/action/Action_Account.kt @@ -3,13 +3,11 @@ package jp.juggler.subwaytooter.action import android.app.Dialog import android.os.Build import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity import jp.juggler.subwaytooter.* import jp.juggler.subwaytooter.api.* import jp.juggler.subwaytooter.api.entity.* -import jp.juggler.subwaytooter.dialog.AccountPicker -import jp.juggler.subwaytooter.dialog.DlgCreateAccount -import jp.juggler.subwaytooter.dialog.DlgTextInput -import jp.juggler.subwaytooter.dialog.LoginForm +import jp.juggler.subwaytooter.dialog.* import jp.juggler.subwaytooter.table.SavedAccount import jp.juggler.subwaytooter.table.UserRelation import jp.juggler.subwaytooter.util.LinkHelper @@ -17,318 +15,357 @@ import jp.juggler.subwaytooter.util.openBrowser import jp.juggler.util.* object Action_Account { - - @Suppress("unused") - private val log = LogCategory("Action_Account") - - // アカウントの追加 - fun add(activity : ActMain) { - - LoginForm.showLoginForm( - activity, - null - ) { dialog, instance, action -> - TootTaskRunner(activity).run(instance, object : TootTask { - - override suspend fun background(client : TootApiClient) : TootApiResult? { - return when(action) { - - LoginForm.Action.Existing -> - client.authentication1(Pref.spClientName(activity)) - - LoginForm.Action.Create -> - client.createUser1(Pref.spClientName(activity)) - - LoginForm.Action.Pseudo, - LoginForm.Action.Token -> { - val (ti, ri) = TootInstance.get(client) - if(ti != null) ri?.data = ti - ri - } - } - } - - override suspend fun handleResult(result : TootApiResult?) { - - result ?: return // cancelled. - - val data = result.data - if(result.error == null && data != null) { - when(action) { - LoginForm.Action.Existing -> if(data is String) { - // ブラウザ用URLが生成された - activity.openBrowser(data.toUri()) - dialog.dismissSafe() - return - } - - LoginForm.Action.Create -> if(data is JsonObject) { - // インスタンスを確認できた - createAccount( - activity, - instance, - data, - dialog - ) - return - } - - LoginForm.Action.Pseudo -> if(data is TootInstance) { - addPseudoAccount( - activity, - instance, - instanceInfo = data - ) { a -> - activity.showToast(false, R.string.server_confirmed) - val pos = activity.app_state.columnCount - activity.addColumn(pos, a, ColumnType.LOCAL) - dialog.dismissSafe() - } - } - - LoginForm.Action.Token -> if(data is TootInstance) { - DlgTextInput.show( - activity, - activity.getString(R.string.access_token_or_api_token), - null, - callback = object : DlgTextInput.Callback { - - @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") - override fun onOK( - dialog_token : Dialog, - text : String - ) { - - // dialog引数が二つあるのに注意 - activity.checkAccessToken( - dialog, - dialog_token, - instance, - text, - null - ) - - } - - override fun onEmptyError() { - activity.showToast(true, R.string.token_not_specified) - } - } - ) - } - } - } - - val errorText = result.error ?: "(no error information)" - if(errorText.contains("SSLHandshakeException") - && (Build.VERSION.RELEASE.startsWith("7.0") - || Build.VERSION.RELEASE.startsWith("7.1") - && ! Build.VERSION.RELEASE.startsWith("7.1.") - ) - ) { - AlertDialog.Builder(activity) - .setMessage(errorText + "\n\n" + activity.getString(R.string.ssl_bug_7_0)) - .setNeutralButton(R.string.close, null) - .show() - } else { - activity.showToast(true, "$errorText ${result.requestInfo}".trim()) - } - } - }) - } - } - - private fun createAccount( - activity : ActMain, - instance : Host, - client_info : JsonObject, - dialog_host : Dialog - ) { - DlgCreateAccount( - activity, - instance - ) { dialog_create, username, email, password, agreement, reason -> - // dialog引数が二つあるのに注意 - TootTaskRunner(activity).run(instance, object : TootTask { - - var ta : TootAccount? = null - var ti : TootInstance? = null - - override suspend fun background(client : TootApiClient) : TootApiResult? { - val r1 = client.createUser2Mastodon( - client_info, - username, - email, - password, - agreement, - reason - ) - val ti = r1?.jsonObject ?: return r1 - - val misskeyVersion = TootInstance.parseMisskeyVersion(ti) - - val parser = TootParser( - activity, - linkHelper = LinkHelper.create(instance, misskeyVersion = misskeyVersion) - ) - - this.ti = TootInstance(parser, ti) - - val access_token = ti.string("access_token") - ?: return TootApiResult("can't get user access token") - - val r2 = client.getUserCredential(access_token, misskeyVersion = misskeyVersion) - this.ta = parser.account(r2?.jsonObject) - if(this.ta != null) return r2 - - val jsonObject = jsonObject { - put("id", EntityId.CONFIRMING.toString()) - put("username", username) - put("acct", username) - put("acct", username) - put("url", "https://$instance/@$username") - } - - this.ta = parser.account(jsonObject) - r1.data = jsonObject - r1.tokenInfo = ti - return r1 - } - - override suspend fun handleResult(result : TootApiResult?) { - val sa : SavedAccount? = null - if(activity.afterAccountVerify(result, ta, sa, ti, instance)) { - dialog_host.dismissSafe() - dialog_create.dismissSafe() - } - } - }) - }.show() - } - - // アカウント設定 - fun setting(activity : ActMain) { - AccountPicker.pick( - activity, - bAllowPseudo = true, - bAuto = true, - message = activity.getString(R.string.account_picker_open_setting) - ) { ai -> ActAccountSetting.open(activity, ai, ActMain.REQUEST_CODE_ACCOUNT_SETTING) } - } - - // アカウントを選んでタイムラインカラムを追加 - fun timeline( - activity : ActMain, - pos : Int, - type : ColumnType, - args : Array = emptyArray() - ) { - - AccountPicker.pick( - activity, - bAllowPseudo = type.bAllowPseudo, - bAllowMisskey = type.bAllowMisskey, - bAllowMastodon = type.bAllowMastodon, - bAuto = true, - message = activity.getString( - R.string.account_picker_add_timeline_of, - type.name1(activity) - ) - ) { ai -> - when(type) { - - ColumnType.PROFILE -> { - val id = ai.loginAccount?.id - if(id != null) activity.addColumn(pos, ai, type, id) - } - - ColumnType.PROFILE_DIRECTORY -> - activity.addColumn(pos, ai, type, ai.apiHost) - - else -> activity.addColumn(pos, ai, type, *args) - } - } - } - - // 投稿画面を開く。初期テキストを指定する - fun openPost( - activity : ActMain, - initial_text : String? = activity.quickTootText - ) { - activity.post_helper.closeAcctPopup() - - val db_id = activity.currentPostTarget?.db_id ?: - 1L - if(db_id != - 1L) { - ActPost.open(activity, ActMain.REQUEST_CODE_POST, db_id, initial_text = initial_text) - } else { - AccountPicker.pick( - activity, - bAllowPseudo = false, - bAuto = true, - message = activity.getString(R.string.account_picker_toot) - ) { ai -> - ActPost.open( - activity, - ActMain.REQUEST_CODE_POST, - ai.db_id, - initial_text = initial_text - ) - } - } - } - - fun endorse( - activity : ActMain, - access_info : SavedAccount, - who : TootAccount, - bSet : Boolean - ) { - if(access_info.isMisskey) { - activity.showToast(false, "This feature is not provided on Misskey account.") - return - } - - - TootTaskRunner(activity).run(access_info, object : TootTask { - - var relation : UserRelation? = null - - override suspend fun background(client : TootApiClient) : TootApiResult? { - val result = client.request( - "/api/v1/accounts/${who.id}/" + when(bSet) { - true -> "pin" - false -> "unpin" - }, - "".toFormRequestBody().toPost() - ) - val jsonObject = result?.jsonObject - if(jsonObject != null) { - val tr = parseItem( - ::TootRelationShip, - TootParser(client.context, access_info), - jsonObject - ) - if(tr != null) { - this.relation = saveUserRelation(access_info, tr) - } - } - return result - } - - override suspend fun handleResult(result : TootApiResult?) { - result ?: return - - if(result.error != null) { - activity.showToast(true, result.error) - } else { - activity.showToast( - false, when(bSet) { - true -> R.string.endorse_succeeded - else -> R.string.remove_endorse_succeeded - } - ) - } - } - }) - } + + @Suppress("unused") + private val log = LogCategory("Action_Account") + + // アカウントの追加 + fun add(activity: ActMain) { + + LoginForm.showLoginForm( + activity, + null + ) { dialog, instance, action -> + TootTaskRunner(activity).run(instance, object : TootTask { + + override suspend fun background(client: TootApiClient): TootApiResult? { + return when (action) { + + LoginForm.Action.Existing -> + client.authentication1(Pref.spClientName(activity)) + + LoginForm.Action.Create -> + client.createUser1(Pref.spClientName(activity)) + + LoginForm.Action.Pseudo, + LoginForm.Action.Token -> { + val (ti, ri) = TootInstance.get(client) + if (ti != null) ri?.data = ti + ri + } + } + } + + override suspend fun handleResult(result: TootApiResult?) { + + result ?: return // cancelled. + + val data = result.data + if (result.error == null && data != null) { + when (action) { + LoginForm.Action.Existing -> if (data is String) { + // ブラウザ用URLが生成された + activity.openBrowser(data.toUri()) + dialog.dismissSafe() + return + } + + LoginForm.Action.Create -> if (data is JsonObject) { + // インスタンスを確認できた + createAccount( + activity, + instance, + data, + dialog + ) + return + } + + LoginForm.Action.Pseudo -> if (data is TootInstance) { + addPseudoAccount( + activity, + instance, + instanceInfo = data + ) { a -> + activity.showToast(false, R.string.server_confirmed) + val pos = activity.app_state.columnCount + activity.addColumn(pos, a, ColumnType.LOCAL) + dialog.dismissSafe() + } + } + + LoginForm.Action.Token -> if (data is TootInstance) { + DlgTextInput.show( + activity, + activity.getString(R.string.access_token_or_api_token), + null, + callback = object : DlgTextInput.Callback { + + @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") + override fun onOK( + dialog_token: Dialog, + text: String + ) { + + // dialog引数が二つあるのに注意 + activity.checkAccessToken( + dialog, + dialog_token, + instance, + text, + null + ) + + } + + override fun onEmptyError() { + activity.showToast(true, R.string.token_not_specified) + } + } + ) + } + } + } + + val errorText = result.error ?: "(no error information)" + if (errorText.contains("SSLHandshakeException") + && (Build.VERSION.RELEASE.startsWith("7.0") + || Build.VERSION.RELEASE.startsWith("7.1") + && !Build.VERSION.RELEASE.startsWith("7.1.") + ) + ) { + AlertDialog.Builder(activity) + .setMessage(errorText + "\n\n" + activity.getString(R.string.ssl_bug_7_0)) + .setNeutralButton(R.string.close, null) + .show() + } else { + activity.showToast(true, "$errorText ${result.requestInfo}".trim()) + } + } + }) + } + } + + private fun createAccount( + activity: ActMain, + apiHost: Host, + client_info: JsonObject, + dialog_host: Dialog + ) { + DlgCreateAccount( + activity, + apiHost + ) { dialog_create, username, email, password, agreement, reason -> + // dialog引数が二つあるのに注意 + TootTaskRunner(activity).run(apiHost, object : TootTask { + + var ta: TootAccount? = null + var apDomain: Host? = null + + override suspend fun background(client: TootApiClient): TootApiResult? { + val r1 = client.createUser2Mastodon( + client_info, + username, + email, + password, + agreement, + reason + ) + val tokenJson = r1?.jsonObject ?: return r1 + val misskeyVersion = TootInstance.parseMisskeyVersion(tokenJson) + val parser = TootParser( + activity, + linkHelper = LinkHelper.create(apiHost, misskeyVersion = misskeyVersion) + ) + + // ここだけMastodon専用 + val access_token = tokenJson.string("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) + this.ta = parser.account(r2?.jsonObject) + if (this.ta != null) return r2 + + val jsonObject = jsonObject { + put("id", EntityId.CONFIRMING.toString()) + put("username", username) + put("acct", username) + put("url", "https://$apiHost/@$username") + } + + this.ta = parser.account(jsonObject) + r1.data = jsonObject + r1.tokenInfo = tokenJson + return r1 + } + + override suspend fun handleResult(result: TootApiResult?) { + val sa: SavedAccount? = null + if (activity.afterAccountVerify(result, ta, sa, apiHost, apDomain)) { + dialog_host.dismissSafe() + dialog_create.dismissSafe() + } + } + }) + }.show() + } + + // アカウント設定 + fun setting(activity: ActMain) { + AccountPicker.pick( + activity, + bAllowPseudo = true, + bAuto = true, + message = activity.getString(R.string.account_picker_open_setting) + ) { ai -> ActAccountSetting.open(activity, ai, ActMain.REQUEST_CODE_ACCOUNT_SETTING) } + } + + // アカウントを選んでタイムラインカラムを追加 + fun timeline( + activity: ActMain, + pos: Int, + type: ColumnType, + args: Array = emptyArray() + ) { + + AccountPicker.pick( + activity, + bAllowPseudo = type.bAllowPseudo, + bAllowMisskey = type.bAllowMisskey, + bAllowMastodon = type.bAllowMastodon, + bAuto = true, + message = activity.getString( + R.string.account_picker_add_timeline_of, + type.name1(activity) + ) + ) { ai -> + when (type) { + + ColumnType.PROFILE -> { + val id = ai.loginAccount?.id + if (id != null) activity.addColumn(pos, ai, type, id) + } + + ColumnType.PROFILE_DIRECTORY -> + activity.addColumn(pos, ai, type, ai.apiHost) + + else -> activity.addColumn(pos, ai, type, *args) + } + } + } + + // 投稿画面を開く。初期テキストを指定する + fun openPost( + activity: ActMain, + initial_text: String? = activity.quickTootText + ) { + activity.post_helper.closeAcctPopup() + + val db_id = activity.currentPostTarget?.db_id ?: -1L + if (db_id != -1L) { + ActPost.open(activity, ActMain.REQUEST_CODE_POST, db_id, initial_text = initial_text) + } else { + AccountPicker.pick( + activity, + bAllowPseudo = false, + bAuto = true, + message = activity.getString(R.string.account_picker_toot) + ) { ai -> + ActPost.open( + activity, + ActMain.REQUEST_CODE_POST, + ai.db_id, + initial_text = initial_text + ) + } + } + } + + fun endorse( + activity: ActMain, + access_info: SavedAccount, + who: TootAccount, + bSet: Boolean + ) { + if (access_info.isMisskey) { + activity.showToast(false, "This feature is not provided on Misskey account.") + return + } + + + TootTaskRunner(activity).run(access_info, object : TootTask { + + var relation: UserRelation? = null + + override suspend fun background(client: TootApiClient): TootApiResult? { + val result = client.request( + "/api/v1/accounts/${who.id}/" + when (bSet) { + true -> "pin" + false -> "unpin" + }, + "".toFormRequestBody().toPost() + ) + val jsonObject = result?.jsonObject + if (jsonObject != null) { + val tr = parseItem( + ::TootRelationShip, + TootParser(client.context, access_info), + jsonObject + ) + if (tr != null) { + this.relation = saveUserRelation(access_info, tr) + } + } + return result + } + + override suspend fun handleResult(result: TootApiResult?) { + result ?: return + + if (result.error != null) { + activity.showToast(true, result.error) + } else { + activity.showToast( + false, when (bSet) { + true -> R.string.endorse_succeeded + else -> R.string.remove_endorse_succeeded + } + ) + } + } + }) + } + + 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().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() + } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootInstance.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootInstance.kt index 2a03f883..680984af 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootInstance.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootInstance.kt @@ -475,7 +475,7 @@ class TootInstance(parser: TootParser, src: JsonObject) { TootParser( client.context, linkHelper = linkHelper ?: LinkHelper.create( - hostArg!!, + (hostArg ?: client.apiHost)!!, misskeyVersion = parseMisskeyVersion(json) ) ), diff --git a/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgConfirmMail.kt b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgConfirmMail.kt new file mode 100644 index 00000000..359faec6 --- /dev/null +++ b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgConfirmMail.kt @@ -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(R.id.tvUserName).text = accessInfo.acct.pretty + + viewRoot.findViewById(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