feat: authorize notes for listed (#1823)
This commit is contained in:
@@ -16,6 +16,7 @@ export class SNNote extends DecryptedItem<NoteContent> implements NoteContentSpe
|
|||||||
public readonly preview_html: string
|
public readonly preview_html: string
|
||||||
public readonly spellcheck?: boolean
|
public readonly spellcheck?: boolean
|
||||||
public readonly noteType?: NoteType
|
public readonly noteType?: NoteType
|
||||||
|
public readonly authorizedForListed: boolean
|
||||||
|
|
||||||
/** The package_info.identifier of the editor (not its uuid), such as org.standardnotes.advanced-markdown */
|
/** The package_info.identifier of the editor (not its uuid), such as org.standardnotes.advanced-markdown */
|
||||||
public readonly editorIdentifier?: FeatureIdentifier | string
|
public readonly editorIdentifier?: FeatureIdentifier | string
|
||||||
@@ -31,6 +32,7 @@ export class SNNote extends DecryptedItem<NoteContent> implements NoteContentSpe
|
|||||||
this.spellcheck = this.payload.content.spellcheck
|
this.spellcheck = this.payload.content.spellcheck
|
||||||
this.noteType = this.payload.content.noteType
|
this.noteType = this.payload.content.noteType
|
||||||
this.editorIdentifier = this.payload.content.editorIdentifier
|
this.editorIdentifier = this.payload.content.editorIdentifier
|
||||||
|
this.authorizedForListed = this.payload.content.authorizedForListed || false
|
||||||
|
|
||||||
if (!this.noteType) {
|
if (!this.noteType) {
|
||||||
const prefersPlain = this.getAppDomainValueWithDefault(AppDataField.LegacyPrefersPlainEditor, false)
|
const prefersPlain = this.getAppDomainValueWithDefault(AppDataField.LegacyPrefersPlainEditor, false)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface NoteContentSpecialized {
|
|||||||
spellcheck?: boolean
|
spellcheck?: boolean
|
||||||
noteType?: NoteType
|
noteType?: NoteType
|
||||||
editorIdentifier?: FeatureIdentifier | string
|
editorIdentifier?: FeatureIdentifier | string
|
||||||
|
authorizedForListed?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NoteContent = NoteContentSpecialized & ItemContent
|
export type NoteContent = NoteContentSpecialized & ItemContent
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ export class NoteMutator extends DecryptedItemMutator<NoteContent> {
|
|||||||
this.mutableContent.editorIdentifier = identifier
|
this.mutableContent.editorIdentifier = identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set authorizedForListed(authorizedForListed: boolean) {
|
||||||
|
this.mutableContent.authorizedForListed = authorizedForListed
|
||||||
|
}
|
||||||
|
|
||||||
toggleSpellcheck(): void {
|
toggleSpellcheck(): void {
|
||||||
if (this.mutableContent.spellcheck == undefined) {
|
if (this.mutableContent.spellcheck == undefined) {
|
||||||
this.mutableContent.spellcheck = false
|
this.mutableContent.spellcheck = false
|
||||||
|
|||||||
@@ -24,4 +24,5 @@ export enum ChallengeReason {
|
|||||||
UnprotectFile,
|
UnprotectFile,
|
||||||
UnprotectNote,
|
UnprotectNote,
|
||||||
DeleteAccount,
|
DeleteAccount,
|
||||||
|
AuthorizeNoteForListed,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ import { ClientDisplayableError } from '@standardnotes/responses'
|
|||||||
|
|
||||||
import { SnjsVersion } from './../Version'
|
import { SnjsVersion } from './../Version'
|
||||||
import { SNLog } from '../Log'
|
import { SNLog } from '../Log'
|
||||||
import { Challenge, ChallengeResponse } from '../Services'
|
import { Challenge, ChallengeResponse, ListedClientInterface } from '../Services'
|
||||||
import { ApplicationConstructorOptions, FullyResolvedApplicationOptions } from './Options/ApplicationOptions'
|
import { ApplicationConstructorOptions, FullyResolvedApplicationOptions } from './Options/ApplicationOptions'
|
||||||
import { ApplicationOptionsDefaults } from './Options/Defaults'
|
import { ApplicationOptionsDefaults } from './Options/Defaults'
|
||||||
|
|
||||||
@@ -86,9 +86,7 @@ type ApplicationObserver = {
|
|||||||
|
|
||||||
type ObserverRemover = () => void
|
type ObserverRemover = () => void
|
||||||
|
|
||||||
export class SNApplication
|
export class SNApplication implements ApplicationInterface, AppGroupManagedApplication {
|
||||||
implements ApplicationInterface, AppGroupManagedApplication, InternalServices.ListedClientInterface
|
|
||||||
{
|
|
||||||
onDeinit!: ExternalServices.DeinitCallback
|
onDeinit!: ExternalServices.DeinitCallback
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -273,6 +271,10 @@ export class SNApplication
|
|||||||
return this.componentManagerService
|
return this.componentManagerService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get listed(): ListedClientInterface {
|
||||||
|
return this.listedService
|
||||||
|
}
|
||||||
|
|
||||||
public computePrivateUsername(username: string): Promise<string | undefined> {
|
public computePrivateUsername(username: string): Promise<string | undefined> {
|
||||||
return ComputePrivateUsername(this.options.crypto, username)
|
return ComputePrivateUsername(this.options.crypto, username)
|
||||||
}
|
}
|
||||||
@@ -682,25 +684,6 @@ export class SNApplication
|
|||||||
return this.protectionService.authorizeSearchingProtectedNotesText()
|
return this.protectionService.authorizeSearchingProtectedNotesText()
|
||||||
}
|
}
|
||||||
|
|
||||||
public canRegisterNewListedAccount(): boolean {
|
|
||||||
return this.listedService.canRegisterNewListedAccount()
|
|
||||||
}
|
|
||||||
|
|
||||||
public async requestNewListedAccount(): Promise<Responses.ListedAccount | undefined> {
|
|
||||||
return this.listedService.requestNewListedAccount()
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getListedAccounts(): Promise<Responses.ListedAccount[]> {
|
|
||||||
return this.listedService.getListedAccounts()
|
|
||||||
}
|
|
||||||
|
|
||||||
public getListedAccountInfo(
|
|
||||||
account: Responses.ListedAccount,
|
|
||||||
inContextOfItem?: UuidString,
|
|
||||||
): Promise<Responses.ListedAccountInfo | undefined> {
|
|
||||||
return this.listedService.getListedAccountInfo(account, inContextOfItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
public async createEncryptedBackupFileForAutomatedDesktopBackups(): Promise<BackupFile | undefined> {
|
public async createEncryptedBackupFileForAutomatedDesktopBackups(): Promise<BackupFile | undefined> {
|
||||||
return this.protocolService.createEncryptedBackupFile()
|
return this.protocolService.createEncryptedBackupFile()
|
||||||
}
|
}
|
||||||
@@ -1096,11 +1079,11 @@ export class SNApplication
|
|||||||
this.createComponentManager()
|
this.createComponentManager()
|
||||||
this.createMigrationService()
|
this.createMigrationService()
|
||||||
this.createMfaService()
|
this.createMfaService()
|
||||||
this.createListedService()
|
|
||||||
this.createActionsManager()
|
|
||||||
this.createFileService()
|
this.createFileService()
|
||||||
this.createIntegrityService()
|
this.createIntegrityService()
|
||||||
this.createMutatorService()
|
this.createMutatorService()
|
||||||
|
this.createListedService()
|
||||||
|
this.createActionsManager()
|
||||||
this.createStatusService()
|
this.createStatusService()
|
||||||
|
|
||||||
if (isDesktopDevice(this.deviceInterface)) {
|
if (isDesktopDevice(this.deviceInterface)) {
|
||||||
@@ -1175,6 +1158,8 @@ export class SNApplication
|
|||||||
this.itemManager,
|
this.itemManager,
|
||||||
this.settingsService,
|
this.settingsService,
|
||||||
this.deprecatedHttpService,
|
this.deprecatedHttpService,
|
||||||
|
this.protectionService,
|
||||||
|
this.mutator,
|
||||||
this.internalEventBus,
|
this.internalEventBus,
|
||||||
)
|
)
|
||||||
this.services.push(this.listedService)
|
this.services.push(this.listedService)
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ export const ChallengeStrings = {
|
|||||||
SelectProtectedNote: 'Authentication is required to select a protected note',
|
SelectProtectedNote: 'Authentication is required to select a protected note',
|
||||||
DisableMfa: 'Authentication is required to disable two-factor authentication',
|
DisableMfa: 'Authentication is required to disable two-factor authentication',
|
||||||
DeleteAccount: 'Authentication is required to delete your account',
|
DeleteAccount: 'Authentication is required to delete your account',
|
||||||
|
ListedAuthorization: 'Authentication is required to approve this note for Listed',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ErrorAlertStrings = {
|
export const ErrorAlertStrings = {
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ export class Challenge implements ChallengeInterface {
|
|||||||
return ChallengeStrings.DisableMfa
|
return ChallengeStrings.DisableMfa
|
||||||
case ChallengeReason.DeleteAccount:
|
case ChallengeReason.DeleteAccount:
|
||||||
return ChallengeStrings.DeleteAccount
|
return ChallengeStrings.DeleteAccount
|
||||||
|
case ChallengeReason.AuthorizeNoteForListed:
|
||||||
|
return ChallengeStrings.ListedAuthorization
|
||||||
case ChallengeReason.Custom:
|
case ChallengeReason.Custom:
|
||||||
return ''
|
return ''
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { SNNote } from '@standardnotes/models'
|
||||||
import { Uuid } from '@standardnotes/common'
|
import { Uuid } from '@standardnotes/common'
|
||||||
import { ListedAccount, ListedAccountInfo } from '@standardnotes/responses'
|
import { ListedAccount, ListedAccountInfo } from '@standardnotes/responses'
|
||||||
|
|
||||||
@@ -6,4 +7,6 @@ export interface ListedClientInterface {
|
|||||||
requestNewListedAccount: () => Promise<ListedAccount | undefined>
|
requestNewListedAccount: () => Promise<ListedAccount | undefined>
|
||||||
getListedAccounts(): Promise<ListedAccount[]>
|
getListedAccounts(): Promise<ListedAccount[]>
|
||||||
getListedAccountInfo(account: ListedAccount, inContextOfItem?: Uuid): Promise<ListedAccountInfo | undefined>
|
getListedAccountInfo(account: ListedAccount, inContextOfItem?: Uuid): Promise<ListedAccountInfo | undefined>
|
||||||
|
isNoteAuthorizedForListed(note: SNNote): boolean
|
||||||
|
authorizeNoteForListed(note: SNNote): Promise<boolean>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import { SNSettingsService } from '../Settings/SNSettingsService'
|
|||||||
import { ListedClientInterface } from './ListedClientInterface'
|
import { ListedClientInterface } from './ListedClientInterface'
|
||||||
import { SNApiService } from '../Api/ApiService'
|
import { SNApiService } from '../Api/ApiService'
|
||||||
import { ListedAccount, ListedAccountInfo, ListedAccountInfoResponse } from '@standardnotes/responses'
|
import { ListedAccount, ListedAccountInfo, ListedAccountInfoResponse } from '@standardnotes/responses'
|
||||||
import { SNActionsExtension } from '@standardnotes/models'
|
import { NoteMutator, SNActionsExtension, SNNote } from '@standardnotes/models'
|
||||||
import { AbstractService, InternalEventBusInterface } from '@standardnotes/services'
|
import { AbstractService, InternalEventBusInterface, MutatorClientInterface } from '@standardnotes/services'
|
||||||
|
import { SNProtectionService } from '../Protection'
|
||||||
|
|
||||||
export class ListedService extends AbstractService implements ListedClientInterface {
|
export class ListedService extends AbstractService implements ListedClientInterface {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -17,6 +18,8 @@ export class ListedService extends AbstractService implements ListedClientInterf
|
|||||||
private itemManager: ItemManager,
|
private itemManager: ItemManager,
|
||||||
private settingsService: SNSettingsService,
|
private settingsService: SNSettingsService,
|
||||||
private httpSerivce: SNHttpService,
|
private httpSerivce: SNHttpService,
|
||||||
|
private protectionService: SNProtectionService,
|
||||||
|
private mutatorService: MutatorClientInterface,
|
||||||
protected override internalEventBus: InternalEventBusInterface,
|
protected override internalEventBus: InternalEventBusInterface,
|
||||||
) {
|
) {
|
||||||
super(internalEventBus)
|
super(internalEventBus)
|
||||||
@@ -27,6 +30,8 @@ export class ListedService extends AbstractService implements ListedClientInterf
|
|||||||
;(this.settingsService as unknown) = undefined
|
;(this.settingsService as unknown) = undefined
|
||||||
;(this.apiService as unknown) = undefined
|
;(this.apiService as unknown) = undefined
|
||||||
;(this.httpSerivce as unknown) = undefined
|
;(this.httpSerivce as unknown) = undefined
|
||||||
|
;(this.protectionService as unknown) = undefined
|
||||||
|
;(this.mutatorService as unknown) = undefined
|
||||||
super.deinit()
|
super.deinit()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +39,23 @@ export class ListedService extends AbstractService implements ListedClientInterf
|
|||||||
return this.apiService.user != undefined
|
return this.apiService.user != undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isNoteAuthorizedForListed(note: SNNote): boolean {
|
||||||
|
return note.authorizedForListed
|
||||||
|
}
|
||||||
|
|
||||||
|
public async authorizeNoteForListed(note: SNNote): Promise<boolean> {
|
||||||
|
const result = await this.protectionService.authorizeListedPublishing()
|
||||||
|
if (result === false) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.mutatorService.changeAndSaveItem<NoteMutator>(note, (mutator) => {
|
||||||
|
mutator.authorizedForListed = true
|
||||||
|
})
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Account creation is asyncronous on the backend due to message-based nature of architecture.
|
* Account creation is asyncronous on the backend due to message-based nature of architecture.
|
||||||
* In order to get the newly created account, we poll the server to check for new accounts.
|
* In order to get the newly created account, we poll the server to check for new accounts.
|
||||||
@@ -73,8 +95,11 @@ export class ListedService extends AbstractService implements ListedClientInterf
|
|||||||
if (inContextOfItem) {
|
if (inContextOfItem) {
|
||||||
url += `&item_uuid=${inContextOfItem}`
|
url += `&item_uuid=${inContextOfItem}`
|
||||||
}
|
}
|
||||||
const response = (await this.httpSerivce.getAbsolute(url)) as ListedAccountInfoResponse
|
|
||||||
if (response.error || !response.data || isString(response.data)) {
|
const response = (await this.httpSerivce.getAbsolute(url).catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
})) as ListedAccountInfoResponse
|
||||||
|
if (!response || response.error || !response.data || isString(response.data)) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -219,13 +219,18 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
|||||||
return this.authorizeAction(ChallengeReason.RevokeSession)
|
return this.authorizeAction(ChallengeReason.RevokeSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async authorizeListedPublishing(): Promise<boolean> {
|
||||||
|
return this.authorizeAction(ChallengeReason.AuthorizeNoteForListed, { forcePrompt: true })
|
||||||
|
}
|
||||||
|
|
||||||
async authorizeAction(
|
async authorizeAction(
|
||||||
reason: ChallengeReason,
|
reason: ChallengeReason,
|
||||||
{ fallBackToAccountPassword = true, requireAccountPassword = false } = {},
|
{ fallBackToAccountPassword = true, requireAccountPassword = false, forcePrompt = false } = {},
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
return this.validateOrRenewSession(reason, {
|
return this.validateOrRenewSession(reason, {
|
||||||
requireAccountPassword,
|
requireAccountPassword,
|
||||||
fallBackToAccountPassword,
|
fallBackToAccountPassword,
|
||||||
|
forcePrompt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,9 +300,9 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
|||||||
|
|
||||||
private async validateOrRenewSession(
|
private async validateOrRenewSession(
|
||||||
reason: ChallengeReason,
|
reason: ChallengeReason,
|
||||||
{ fallBackToAccountPassword = true, requireAccountPassword = false } = {},
|
{ fallBackToAccountPassword = true, requireAccountPassword = false, forcePrompt = false } = {},
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (this.getSessionExpiryDate() > new Date()) {
|
if (this.getSessionExpiryDate() > new Date() && !forcePrompt) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,27 @@ type ListedActionsMenuProps = {
|
|||||||
const ListedActionsMenu = ({ application, note }: ListedActionsMenuProps) => {
|
const ListedActionsMenu = ({ application, note }: ListedActionsMenuProps) => {
|
||||||
const [menuGroups, setMenuGroups] = useState<ListedMenuGroup[]>([])
|
const [menuGroups, setMenuGroups] = useState<ListedMenuGroup[]>([])
|
||||||
const [isFetchingAccounts, setIsFetchingAccounts] = useState(true)
|
const [isFetchingAccounts, setIsFetchingAccounts] = useState(true)
|
||||||
|
const [isAuthorized, setIsAuthorized] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const authorize = async () => {
|
||||||
|
if (!application.listed.isNoteAuthorizedForListed(note)) {
|
||||||
|
await application.listed.authorizeNoteForListed(note)
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsAuthorized(application.listed.isNoteAuthorizedForListed(note))
|
||||||
|
}
|
||||||
|
|
||||||
|
void authorize()
|
||||||
|
}, [application, note])
|
||||||
|
|
||||||
const reloadMenuGroup = useCallback(
|
const reloadMenuGroup = useCallback(
|
||||||
async (group: ListedMenuGroup) => {
|
async (group: ListedMenuGroup) => {
|
||||||
const updatedAccountInfo = await application.getListedAccountInfo(group.account, note.uuid)
|
if (!isAuthorized) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedAccountInfo = await application.listed.getListedAccountInfo(group.account, note.uuid)
|
||||||
|
|
||||||
if (!updatedAccountInfo) {
|
if (!updatedAccountInfo) {
|
||||||
return
|
return
|
||||||
@@ -39,7 +56,7 @@ const ListedActionsMenu = ({ application, note }: ListedActionsMenuProps) => {
|
|||||||
|
|
||||||
setMenuGroups(updatedGroups)
|
setMenuGroups(updatedGroups)
|
||||||
},
|
},
|
||||||
[application, menuGroups, note],
|
[application, menuGroups, note, isAuthorized],
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -49,19 +66,21 @@ const ListedActionsMenu = ({ application, note }: ListedActionsMenuProps) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isAuthorized) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const listedAccountEntries = await application.getListedAccounts()
|
const listedAccountEntries = await application.listed.getListedAccounts()
|
||||||
|
|
||||||
if (!listedAccountEntries.length) {
|
if (!listedAccountEntries.length) {
|
||||||
throw new Error('No Listed accounts found')
|
throw new Error('No Listed accounts found')
|
||||||
}
|
}
|
||||||
|
|
||||||
const menuGroups: ListedMenuGroup[] = []
|
const menuGroups: ListedMenuGroup[] = []
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
listedAccountEntries.map(async (account) => {
|
listedAccountEntries.map(async (account) => {
|
||||||
const accountInfo = await application.getListedAccountInfo(account, note.uuid)
|
const accountInfo = await application.listed.getListedAccountInfo(account, note.uuid)
|
||||||
|
|
||||||
if (accountInfo) {
|
if (accountInfo) {
|
||||||
menuGroups.push({
|
menuGroups.push({
|
||||||
name: accountInfo.display_name,
|
name: accountInfo.display_name,
|
||||||
@@ -91,7 +110,11 @@ const ListedActionsMenu = ({ application, note }: ListedActionsMenuProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fetchListedAccounts()
|
void fetchListedAccounts()
|
||||||
}, [application, note.uuid])
|
}, [application, note.uuid, isAuthorized])
|
||||||
|
|
||||||
|
if (!isAuthorized) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -17,9 +17,15 @@ const ListedActionsOption: FunctionComponent<Props> = ({ application, note }) =>
|
|||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
|
||||||
const toggleMenu = useCallback(() => {
|
const toggleMenu = useCallback(async () => {
|
||||||
setIsOpen((isOpen) => !isOpen)
|
if (!application.listed.isNoteAuthorizedForListed(note)) {
|
||||||
}, [])
|
await application.listed.authorizeNoteForListed(note)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (application.listed.isNoteAuthorizedForListed(note)) {
|
||||||
|
setIsOpen((isOpen) => !isOpen)
|
||||||
|
}
|
||||||
|
}, [application, note])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={menuContainerRef}>
|
<div ref={menuContainerRef}>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const Listed = ({ application }: Props) => {
|
|||||||
const [requestingAccount, setRequestingAccount] = useState<boolean>()
|
const [requestingAccount, setRequestingAccount] = useState<boolean>()
|
||||||
|
|
||||||
const reloadAccounts = useCallback(async () => {
|
const reloadAccounts = useCallback(async () => {
|
||||||
setAccounts(await application.getListedAccounts())
|
setAccounts(await application.listed.getListedAccounts())
|
||||||
}, [application])
|
}, [application])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -30,7 +30,7 @@ const Listed = ({ application }: Props) => {
|
|||||||
setRequestingAccount(true)
|
setRequestingAccount(true)
|
||||||
|
|
||||||
const requestAccount = async () => {
|
const requestAccount = async () => {
|
||||||
const account = await application.requestNewListedAccount()
|
const account = await application.listed.requestNewListedAccount()
|
||||||
if (account) {
|
if (account) {
|
||||||
const openSettings = await application.alertService.confirm(
|
const openSettings = await application.alertService.confirm(
|
||||||
'Your new Listed blog has been successfully created!' +
|
'Your new Listed blog has been successfully created!' +
|
||||||
@@ -43,7 +43,7 @@ const Listed = ({ application }: Props) => {
|
|||||||
)
|
)
|
||||||
reloadAccounts().catch(console.error)
|
reloadAccounts().catch(console.error)
|
||||||
if (openSettings) {
|
if (openSettings) {
|
||||||
const info = await application.getListedAccountInfo(account)
|
const info = await application.listed.getListedAccountInfo(account)
|
||||||
if (info) {
|
if (info) {
|
||||||
application.deviceInterface.openUrl(info?.settings_url)
|
application.deviceInterface.openUrl(info?.settings_url)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const ListedAccountItem: FunctionComponent<Props> = ({ account, showSeparator, a
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadAccount = async () => {
|
const loadAccount = async () => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const info = await application.getListedAccountInfo(account)
|
const info = await application.listed.getListedAccountInfo(account)
|
||||||
setAccountInfo(info)
|
setAccountInfo(info)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user