feat: add sending user requests from UI (#1927)

* feat: add sending user requests from UI

* fix(web): view controller manager user client references
This commit is contained in:
Karol Sójko
2022-11-03 09:39:38 +01:00
committed by GitHub
parent 6b50372db2
commit 7ead0f655b
15 changed files with 278 additions and 7 deletions

View File

@@ -167,6 +167,7 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
application.sessions,
application.subscriptions,
this.toastService,
application.user,
)
this.addAppEventObserver()

View File

@@ -17,6 +17,8 @@ import {
SyncClientInterface,
SyncOpStatus,
User,
UserClientInterface,
UserRequestType,
} from '@standardnotes/snjs'
import { AccountMenuController } from '@/Controllers/AccountMenu/AccountMenuController'
@@ -39,6 +41,7 @@ describe('ApplicationEventObserver', () => {
let sessionManager: SessionsClientInterface
let subscriptionManager: SubscriptionClientInterface
let toastService: ToastServiceInterface
let userService: UserClientInterface
const createObserver = () =>
new ApplicationEventObserver(
@@ -52,6 +55,7 @@ describe('ApplicationEventObserver', () => {
sessionManager,
subscriptionManager,
toastService,
userService,
)
beforeEach(() => {
@@ -87,7 +91,11 @@ describe('ApplicationEventObserver', () => {
subscriptionManager.acceptInvitation = jest.fn()
toastService = {} as jest.Mocked<ToastServiceInterface>
toastService.showToast = jest.fn()
toastService.showToast = jest.fn().mockReturnValue('1')
toastService.hideToast = jest.fn()
userService = {} as jest.Mocked<UserClientInterface>
userService.submitUserRequest = jest.fn().mockReturnValue(true)
})
describe('Upon Application Launched', () => {
@@ -184,6 +192,63 @@ describe('ApplicationEventObserver', () => {
expect(toastService.showToast).toHaveBeenCalledWith(ToastType.Error, 'Oops!')
expect(routeService.removeQueryParameterFromURL).toHaveBeenCalledWith(RootQueryParam.AcceptSubscriptionInvite)
})
it('should open up sign in if user is not logged in and tries to send request', async () => {
sessionManager.getUser = jest.fn().mockReturnValue(undefined)
routeService.getRoute = jest.fn().mockReturnValue({
type: RouteType.UserRequest,
userRequestParams: {
requestType: UserRequestType.ExitDiscount,
},
} as jest.Mocked<RouteParserInterface>)
await createObserver().handle(ApplicationEvent.Launched)
expect(accountMenuController.setShow).toHaveBeenCalledWith(true)
expect(accountMenuController.setCurrentPane).toHaveBeenCalledWith(AccountMenuPane.SignIn)
expect(userService.submitUserRequest).not.toHaveBeenCalled()
expect(toastService.showToast).not.toHaveBeenCalled()
})
it('should send user request if user is logged in', async () => {
userService.submitUserRequest = jest.fn().mockReturnValue(true)
routeService.getRoute = jest.fn().mockReturnValue({
type: RouteType.UserRequest,
userRequestParams: {
requestType: UserRequestType.ExitDiscount,
},
} as jest.Mocked<RouteParserInterface>)
await createObserver().handle(ApplicationEvent.Launched)
expect(userService.submitUserRequest).toHaveBeenCalledWith('exit-discount')
expect(toastService.showToast).toHaveBeenNthCalledWith(
2,
ToastType.Success,
'We have received your request. Please check your email for further instructions.',
)
expect(routeService.removeQueryParameterFromURL).toHaveBeenCalledWith(RootQueryParam.UserRequest)
})
it('should show sending request failure if user is logged in and sending fails', async () => {
userService.submitUserRequest = jest.fn().mockReturnValue(false)
routeService.getRoute = jest.fn().mockReturnValue({
type: RouteType.UserRequest,
userRequestParams: {
requestType: UserRequestType.ExitDiscount,
},
} as jest.Mocked<RouteParserInterface>)
await createObserver().handle(ApplicationEvent.Launched)
expect(userService.submitUserRequest).toHaveBeenCalledWith('exit-discount')
expect(toastService.showToast).toHaveBeenNthCalledWith(
2,
ToastType.Success,
'We could not process your request. Please try again or contact support if the issue persists.',
)
expect(routeService.removeQueryParameterFromURL).toHaveBeenCalledWith(RootQueryParam.UserRequest)
})
})
describe('Upon Signing In', () => {
@@ -219,6 +284,26 @@ describe('ApplicationEventObserver', () => {
)
expect(routeService.removeQueryParameterFromURL).toHaveBeenCalledWith(RootQueryParam.AcceptSubscriptionInvite)
})
it('should send user request', async () => {
userService.submitUserRequest = jest.fn().mockReturnValue(true)
routeService.getRoute = jest.fn().mockReturnValue({
type: RouteType.UserRequest,
userRequestParams: {
requestType: UserRequestType.ExitDiscount,
},
} as jest.Mocked<RouteParserInterface>)
await createObserver().handle(ApplicationEvent.SignedIn)
expect(userService.submitUserRequest).toHaveBeenCalledWith('exit-discount')
expect(toastService.showToast).toHaveBeenNthCalledWith(
2,
ToastType.Success,
'We have received your request. Please check your email for further instructions.',
)
expect(routeService.removeQueryParameterFromURL).toHaveBeenCalledWith(RootQueryParam.UserRequest)
})
})
describe('Upon Sync Status Changing', () => {

View File

@@ -10,6 +10,7 @@ import {
SessionsClientInterface,
SubscriptionClientInterface,
SyncClientInterface,
UserClientInterface,
} from '@standardnotes/snjs'
import { ToastType } from '@standardnotes/toast'
@@ -34,6 +35,7 @@ export class ApplicationEventObserver implements EventObserverInterface {
private sessionManager: SessionsClientInterface,
private subscriptionManager: SubscriptionClientInterface,
private toastService: ToastServiceInterface,
private userService: UserClientInterface,
) {}
async handle(event: ApplicationEvent): Promise<void> {
@@ -67,6 +69,17 @@ export class ApplicationEventObserver implements EventObserverInterface {
}
await this.acceptSubscriptionInvitation(route)
break
}
case RouteType.UserRequest: {
const user = this.sessionManager.getUser()
if (user === undefined) {
this.promptUserSignIn()
break
}
await this.sendUserRequest(route)
break
}
}
@@ -84,6 +97,10 @@ export class ApplicationEventObserver implements EventObserverInterface {
case RouteType.AcceptSubscriptionInvite:
await this.acceptSubscriptionInvitation(route)
break
case RouteType.UserRequest:
await this.sendUserRequest(route)
break
}
}
@@ -105,8 +122,12 @@ export class ApplicationEventObserver implements EventObserverInterface {
}
private async acceptSubscriptionInvitation(route: RouteParserInterface): Promise<void> {
const processingToastId = this.toastService.showToast(ToastType.Loading, 'Accepting invitation...')
const acceptResult = await this.subscriptionManager.acceptInvitation(route.subscriptionInviteParams.inviteUuid)
this.toastService.hideToast(processingToastId)
const toastType = acceptResult.success ? ToastType.Success : ToastType.Error
const toastMessage = acceptResult.success ? 'Successfully joined a shared subscription' : acceptResult.message
@@ -114,4 +135,21 @@ export class ApplicationEventObserver implements EventObserverInterface {
this.routeService.removeQueryParameterFromURL(RootQueryParam.AcceptSubscriptionInvite)
}
private async sendUserRequest(route: RouteParserInterface): Promise<void> {
const processingToastId = this.toastService.showToast(ToastType.Loading, 'Processing your request...')
const requestSubmittedSuccessfully = await this.userService.submitUserRequest(route.userRequestParams.requestType)
this.toastService.hideToast(processingToastId)
const toastType = requestSubmittedSuccessfully ? ToastType.Success : ToastType.Error
const toastMessage = requestSubmittedSuccessfully
? 'We have received your request. Please check your email for further instructions.'
: 'We could not process your request. Please try again or contact support if the issue persists.'
this.toastService.showToast(toastType, toastMessage)
this.routeService.removeQueryParameterFromURL(RootQueryParam.UserRequest)
}
}