add support for logging in to mobile using deep links (#4212)
# What this PR does Optimize mobile app connection layout for smaller screens and add LinkLoginButton component https://www.loom.com/share/1e057464bbe6428c994cf4831713d45c?sid=b5d70863-862e-4ef9-8303-a3354622f442 ## Which issue(s) this PR closes https://github.com/grafana/oncall-private/issues/2528 ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes.
This commit is contained in:
parent
b3c1800f87
commit
395335f21d
8 changed files with 130 additions and 9 deletions
|
|
@ -29,10 +29,21 @@ Mobile app download:
|
|||
- [Google Play Store](https://play.google.com/store/apps/details?id=com.grafana.oncall.prod)
|
||||
- [Apple App Store](https://apps.apple.com/us/app/grafana-oncall-preview/id1669759048)
|
||||
|
||||
## Connect your Grafana OnCall account
|
||||
## Connect your Grafana OnCall account using deeplink authentication
|
||||
|
||||
The OnCall mobile app uses a QR code authentication to connect to your Grafana OnCall instance.
|
||||
You can associate one Grafana OnCall user with your OnCall mobile app.
|
||||
You can connect your Grafana OnCall account to the mobile app using a deeplink authentication.
|
||||
This method is useful because it allows you to connect your account using only your mobile device.
|
||||
|
||||
To connect your account in the mobile app:
|
||||
|
||||
1. Open Grafana OnCall from your mobile device
|
||||
2. Click on your profile icon in the top right corner
|
||||
3. Click on the **IRM** tab
|
||||
4. Click on the **Sign in** button
|
||||
|
||||
## Connect your Grafana OnCall account using QR code authentication
|
||||
|
||||
Another way to connect your Grafana OnCall account to the mobile app is by using a QR code authentication.
|
||||
|
||||
To connect your account in the mobile app:
|
||||
|
||||
|
|
@ -48,8 +59,8 @@ To connect your account in the mobile app:
|
|||
To access your QR code:
|
||||
|
||||
1. Open Grafana OnCall from your desktop
|
||||
1. Navigate to the **Users** tab, then tap **View my profile**
|
||||
1. tap **Mobile app connection** in your profile
|
||||
2. Click on your profile icon in the top right corner
|
||||
3. Click on the **IRM** tab
|
||||
|
||||
> **Note**: The QR code will timeout for security purposes - Screenshots of the QR code are unlikely to work for authentication.
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,22 @@
|
|||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
|
||||
&__box:first-child {
|
||||
margin-right: 0px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
&__box:last-child {
|
||||
margin-left: 0px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notification-buttons {
|
||||
width: 100%;
|
||||
padding-top: 12px;
|
||||
|
|
|
|||
|
|
@ -14,11 +14,12 @@ import { ApiSchemas } from 'network/oncall-api/api.types';
|
|||
import { AppFeature } from 'state/features';
|
||||
import { RootStore, rootStore as store } from 'state/rootStore';
|
||||
import { UserActions } from 'utils/authorization/authorization';
|
||||
import { openErrorNotification, openNotification, openWarningNotification } from 'utils/utils';
|
||||
import { isMobile, openErrorNotification, openNotification, openWarningNotification } from 'utils/utils';
|
||||
|
||||
import styles from './MobileAppConnection.module.scss';
|
||||
import { DisconnectButton } from './parts/DisconnectButton/DisconnectButton';
|
||||
import { DownloadIcons } from './parts/DownloadIcons/DownloadIcons';
|
||||
import { LinkLoginButton } from './parts/LinkLoginButton/LinkLoginButton';
|
||||
import { QRCode } from './parts/QRCode/QRCode';
|
||||
|
||||
const cx = cn.bind(styles);
|
||||
|
|
@ -143,6 +144,7 @@ export const MobileAppConnection = observer(({ userPk }: Props) => {
|
|||
}
|
||||
|
||||
let content: React.ReactNode = null;
|
||||
const QRCodeDataParsed = QRCodeValue && getParsedQRCodeValue();
|
||||
|
||||
if (fetchingQRCode || disconnectingMobileApp || !userPk || !basicDataLoaded) {
|
||||
content = <LoadingPlaceholder text="Loading..." />;
|
||||
|
|
@ -165,12 +167,10 @@ export const MobileAppConnection = observer(({ userPk }: Props) => {
|
|||
</VerticalGroup>
|
||||
);
|
||||
} else if (QRCodeValue) {
|
||||
const QRCodeDataParsed = getParsedQRCodeValue();
|
||||
|
||||
content = (
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text type="primary" strong>
|
||||
Sign In
|
||||
Sign in via QR Code
|
||||
</Text>
|
||||
<Text type="primary">
|
||||
Open the Grafana OnCall mobile application and scan this code to sync it with your account.
|
||||
|
|
@ -198,6 +198,11 @@ export const MobileAppConnection = observer(({ userPk }: Props) => {
|
|||
<Block shadowed bordered withBackground className={cx('container__box')}>
|
||||
<DownloadIcons />
|
||||
</Block>
|
||||
{QRCodeDataParsed && isMobile && (
|
||||
<Block shadowed bordered withBackground className={cx('container__box')}>
|
||||
<LinkLoginButton baseUrl={QRCodeDataParsed.oncall_api_url} token={QRCodeDataParsed.token} />
|
||||
</Block>
|
||||
)}
|
||||
<Block shadowed bordered withBackground className={cx('container__box')}>
|
||||
{content}
|
||||
</Block>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
import React from 'react';
|
||||
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { LinkLoginButton } from './LinkLoginButton';
|
||||
|
||||
describe('LinkLoginButton', () => {
|
||||
test('it renders properly', () => {
|
||||
const component = render(<LinkLoginButton baseUrl="http://test.url" token="test1213" />);
|
||||
expect(component.container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { Button, VerticalGroup } from '@grafana/ui';
|
||||
|
||||
import { Text } from 'components/Text/Text';
|
||||
|
||||
type Props = {
|
||||
baseUrl: string;
|
||||
token: string;
|
||||
};
|
||||
|
||||
export const LinkLoginButton: FC<Props> = (props: Props) => {
|
||||
const { baseUrl, token } = props;
|
||||
const mobileDeepLink = `grafana://mobile/login/link-login?oncall_api_url=${baseUrl}&token=${token}`;
|
||||
|
||||
return (
|
||||
<VerticalGroup spacing="lg">
|
||||
<Text type="primary" strong>
|
||||
Sign in via deeplink
|
||||
</Text>
|
||||
<Text type="primary">Make sure to have the app installed</Text>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
window.open(mobileDeepLink, '_blank');
|
||||
}}
|
||||
>
|
||||
Sign in
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LinkLoginButton it renders properly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="css-gjl87o-vertical-group"
|
||||
style="width: 100%; height: 100%;"
|
||||
>
|
||||
<div
|
||||
class="css-12oo3x0-layoutChildrenWrapper"
|
||||
>
|
||||
<span
|
||||
class="css-77ouhj--primary css-77ouhj--medium css-77ouhj--strong css-1d6rs0l"
|
||||
>
|
||||
Sign in via deeplink
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="css-12oo3x0-layoutChildrenWrapper"
|
||||
>
|
||||
<span
|
||||
class="css-77ouhj--primary css-77ouhj--medium css-1d6rs0l"
|
||||
>
|
||||
Make sure to have the app installed
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="css-12oo3x0-layoutChildrenWrapper"
|
||||
>
|
||||
<button
|
||||
class="css-td06pi-button"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="css-1riaxdn"
|
||||
>
|
||||
Sign in
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -116,3 +116,5 @@ function isFieldEmpty(value: any): boolean {
|
|||
}
|
||||
|
||||
export const allFieldsEmpty = (obj: any) => every(obj, isFieldEmpty);
|
||||
|
||||
export const isMobile = window.matchMedia('(max-width: 768px)').matches;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue