feat(stremio): create updated stremio gdrive guide with OAuth tool to get credentials (#87)
* feat: add stremio gdrive guide with oauth tool * revert: put back es locale * feat: add some logging * feat: use web application instead * feat: update images to reflect change to web application * fix: use correct heading id * trying to fix toasts * idk
@@ -1,14 +1,19 @@
|
||||
---
|
||||
title: Stremio Gdrive
|
||||
title: Stremio GDrive
|
||||
description: Stremio addon for fetching videos from your google drive.
|
||||
keywords: [stremio, addons, http, google drive, guide, setup, configuration, install, link]
|
||||
tags: [stremio-addon, stremio-addon:http-streams]
|
||||
toc_max_heading_level: 2
|
||||
---
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import Details from '@theme/Details';
|
||||
import {CallBackUrl} from '@site/src/components/GoogleOAuth/Callback';
|
||||
|
||||
```mdx-code-block
|
||||
import StremioAddonButtons from '@site/src/components/StremioAddonButtons';
|
||||
|
||||
<StremioAddonButtons id="stremio-gdrive" source="https://github.com/ShuvamJaswal/Gdrive-Stremio-Update" />
|
||||
<StremioAddonButtons id="stremio-gdrive" />
|
||||
```
|
||||
|
||||
<h3>Description</h3>
|
||||
@@ -18,8 +23,194 @@ Stremio-gdrive is a self-hosted Stremio addon that allows you to view files from
|
||||
For it to work, you will need to join some team drives. You can find some [here](https://t.me/s/teamdrives).
|
||||
|
||||
<h3>Configuration</h3>
|
||||
|
||||
I will go through the steps to set up this addon through a Cloudflare Worker.
|
||||
|
||||
As mentioned earlier, this addon needs to be self-hosted.
|
||||
:::tip
|
||||
**This guide should be followed on its [dedicated page](/stremio/addons/stremio-gdrive), not from the setup page.**
|
||||
|
||||
:::
|
||||
|
||||
<h4>Setting up our Google App</h4>
|
||||
|
||||
1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
|
||||
|
||||
If this is your first time using Google Cloud, you will be prompted to agree to the terms of service:
|
||||
|
||||

|
||||
|
||||
2. Create a new project and select it:
|
||||
|
||||
<Details summary="How?">
|
||||
1. Click on `Select a project` in the top left:
|
||||
|
||||

|
||||
|
||||
2. Click on `New Project`:
|
||||
|
||||

|
||||
|
||||
3. Enter a project name and click on `Create`:
|
||||
|
||||

|
||||
|
||||
- The project name can be anything you want, e.g., `Stremio-Gdrive`. Leave the `Organization` field blank.
|
||||
|
||||
4. Once the project has been created, you will get a notification:
|
||||
|
||||

|
||||
|
||||
- Click `Select Project`.
|
||||
|
||||
:::note
|
||||
You may also use the same dropdown from step i to select the project.
|
||||
:::
|
||||
</Details>
|
||||
3. Setup our Google Auth Platform
|
||||
|
||||
1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
|
||||
2. In the search bar at the top, search for `Google Auth Platform` and click on the result:
|
||||
|
||||

|
||||
|
||||
3. You should be met with a message telling you that `Google Auth Platform not configured yet`, click on `Get Started`:
|
||||
|
||||

|
||||
|
||||
4. Fill in the form:
|
||||
|
||||

|
||||
|
||||
1. `App Information`:
|
||||
- Set `App Name` to `Stremio GDrive`.
|
||||
- Set `User Support Email` to your email. It should appear in the dropdown.
|
||||
2. `Audience`:
|
||||
- Set `User Type` to `External`.
|
||||
3. `Contact Information`
|
||||
- Add any email address, you can use the same one you used earlier for `User Support Email`.
|
||||
4. `Finish`
|
||||
- Check the box to agree to the `Google API Services: User Data Policy`
|
||||
|
||||
|
||||
5. Once you have filled in the form, click on `Create`
|
||||
|
||||
4. Enable the Google Drive API.
|
||||
|
||||
1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
|
||||
2. In the search bar at the top, search for `Google Drive API` and click on the result:
|
||||
|
||||

|
||||
|
||||
3. Click on `Enable`:
|
||||
|
||||

|
||||
|
||||
5. Create an OAuth client.
|
||||
|
||||
1. Go back to the `Google Auth Platform` page.
|
||||
2. Click on `Clients` in the sidebar and then click on `+ Create Client`:
|
||||
|
||||

|
||||
|
||||
3. Fill in the form:
|
||||
|
||||

|
||||
|
||||
- `Application Type`: Set this to `Web application`.
|
||||
- `Name`: You can set this to anything such as `Stremio GDrive`.
|
||||
- `Authorized redirect URIs`: Set this to <CallBackUrl />
|
||||
|
||||
|
||||
4. Click on `Create`.
|
||||
|
||||
6. Publish the app.
|
||||
|
||||
1. Go back to the `Google Auth Platform` page.
|
||||
2. Click on `Audience` in the sidebar and then click on `Publish`:
|
||||
|
||||

|
||||
|
||||
|
||||
<h4>Setting up the Cloudflare Worker</h4>
|
||||
|
||||
1. Go to the [Cloudflare Workers](https://workers.cloudflare.com/) page and click `Log In` or `Sign Up` if you don't have an account.
|
||||
|
||||
2. Once logged in, you should be taken to the Cloudflare Workers & Pages dashboard. Click on `Create`:
|
||||
|
||||

|
||||
|
||||
3. Once on the create page, make sure you're on the `Workers` tab and click `Create Worker`:
|
||||
|
||||

|
||||
|
||||
4. You'll be asked to give a name to your worker. You can name it anything, this will be the URL you enter into Stremio to access your addon. Click `Deploy` once named.
|
||||
|
||||

|
||||
|
||||
5. Once its done being deployed, you should be shown a success message. Click the `Edit code` button:
|
||||
|
||||

|
||||
|
||||
6. You should be taken to the Cloudflare Worker editor:
|
||||
|
||||

|
||||
|
||||
7. Now, we will paste the Cloudflare Worker script from the [Stremio GDrive GitHub](https://github.com/Viren070/stremio-gdrive) into the editor.
|
||||
|
||||
1. Go to the [worker.js file](https://github.com/Viren070/stremio-gdrive-addon/blob/main/worker.js)
|
||||
2. Click the Copy icon
|
||||
3. Go back to the Cloudflare Worker editor and paste the script in the editor.
|
||||
|
||||
|
||||
|
||||
|
||||
8. Now, we need to add our Google OAuth credentials to the script.
|
||||
|
||||
To do this, we need to go back to the Google Cloud Console and get our `Client ID` and `Client Secret`.
|
||||
|
||||
1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
|
||||
2. In the search bar at the top, search for `Google Auth Platform` and click on the result:
|
||||
|
||||

|
||||
|
||||
3. Click on `Clients` and click on the download icon for the client you created earlier:
|
||||
|
||||

|
||||
|
||||
4. A pop-up will appear with your `Client ID` and `Client Secret`. Copy these values into the inputs below at the [OAuth Tool](#oauth-tool).
|
||||
|
||||

|
||||
|
||||
5. Click the `Get Authorisation Code` button below to get the Authorisation code.
|
||||
:::tip
|
||||
You will be redirected to a Google OAuth page where you will need to sign in and allow the app to access your Google Drive.
|
||||
You may encounter a warning page, click on `Advanced` and then `Go to... (unsafe)`.
|
||||
:::
|
||||
|
||||
6. Once you have allowed the app, you will be redirected back to this page with the Authorisation code filled in.
|
||||
Click the `Get Credentials` button to get the credentials.
|
||||
|
||||
7. You will be shown a success message and a block of code will appear. Copy this code into the Cloudflare Worker script.
|
||||
|
||||
8. Once you have added your credentials, your script should look something like this:
|
||||
|
||||

|
||||
|
||||
9. Click `Deploy` in the top right to save your changes and deploy the worker.
|
||||
|
||||
11. Once deployed, you should see a green success message at the bottom. Click `Visit` next to the deploy button to go to the addon URL.
|
||||
|
||||
12. You should be redirected to the /manifest.json. if not, append `/manifest.json` to the URL.
|
||||
|
||||
13. Copy the URL and [add it to Stremio](/stremio/faq#how-do-i-install-an-addon-manually).
|
||||
|
||||
|
||||
|
||||
|
||||
### OAuth Tool
|
||||
|
||||
import GoogleOAuth from '@site/src/components/GoogleOAuth';
|
||||
|
||||
<GoogleOAuth />
|
||||
|
||||
You can use [this GitHub repository](https://github.com/ShuvamJaswal/Gdrive-Stremio-Update) to set it up.
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import Callback from '@site/src/components/GoogleOAuth/Callback';
|
||||
|
||||
<Callback />
|
||||
|
||||
# Google Drive Callback
|
||||
|
||||
You will automatically be redirected back to the addon guide after you have authenticated with Google.
|
||||
@@ -0,0 +1,38 @@
|
||||
import React, {useState} from "react";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
import { sessionKeys } from ".";
|
||||
|
||||
export default function Catcher(): JSX.Element {
|
||||
const redirectUrl = useBaseUrl('/stremio/addons/stremio-gdrive#oauth-tool');
|
||||
React.useEffect(() => {
|
||||
const url = new URL(window.location.href);
|
||||
const code = url.searchParams.get("code");
|
||||
if (code) {
|
||||
sessionStorage.setItem(sessionKeys.authorisationCode, code);
|
||||
window.location.href = redirectUrl;
|
||||
}
|
||||
const error = url.searchParams.get("error");
|
||||
if (error) {
|
||||
console.error("Google OAuth Error: ", error);
|
||||
sessionStorage.setItem(sessionKeys.oauthError, error);
|
||||
window.location.href = redirectUrl;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
export function CallBackUrl(): JSX.Element {
|
||||
const callbackPath = useBaseUrl('/stremio/addons/stremio-gdrive/callback');
|
||||
const [redirectUrl, setRedirectUrl] = useState<string>("");
|
||||
React.useEffect(() => {
|
||||
// set the redirect url to the current page
|
||||
setRedirectUrl(window.location.origin + callbackPath);
|
||||
}, []);
|
||||
|
||||
return <>
|
||||
{redirectUrl}
|
||||
</>
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
import React, { useState } from "react";
|
||||
import CodeBlock from "@theme/CodeBlock";
|
||||
import { showToast } from "@site/src/components/Toasts";
|
||||
import styles from "./styles.module.css";
|
||||
import useBaseUrl from "@docusaurus/useBaseUrl";
|
||||
|
||||
const scopes = ['https://www.googleapis.com/auth/drive'];
|
||||
const oauthUrl = "https://accounts.google.com/o/oauth2/v2/auth";
|
||||
const tokenUrl = "https://oauth2.googleapis.com/token";
|
||||
|
||||
export const sessionKeys = {
|
||||
clientId: "Viren070Guides_OAuthTool_clientId",
|
||||
clientSecret: "Viren070Guides_OAuthTool_clientSecret",
|
||||
authorisationCode: "Viren070Guides_OAuthTool_authorisationCode",
|
||||
oauthError: "Viren070Guides_OAuthTool_oauthError"
|
||||
};
|
||||
|
||||
const openAuthorisationUrl = (clientId: string, redirectUrl: string) => {
|
||||
|
||||
if (clientId.length === 0) {
|
||||
showToast("Client ID is required", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(oauthUrl);
|
||||
url.searchParams.append("client_id", clientId);
|
||||
url.searchParams.append("redirect_uri", redirectUrl);
|
||||
url.searchParams.append("response_type", "code");
|
||||
url.searchParams.append("scope", scopes.join(" "));
|
||||
url.searchParams.append("prompt", "consent");
|
||||
url.searchParams.append("access_type", "offline");
|
||||
// open the url in current tab
|
||||
console.log("Opening URL: ", url.toString());
|
||||
window.open(url.toString(), "_self");
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export default function CodeGenerator(): JSX.Element {
|
||||
const [clientId, setClientId] = useState<string>("");
|
||||
const [clientSecret, setClientSecret] = useState<string>("");
|
||||
const [authorisationCode, setAuthorisationCode] = useState<string>("");
|
||||
const [refreshToken, setRefreshToken] = useState<string>("");
|
||||
const [redirectUrl, setRedirectUrl] = useState<string>("");
|
||||
|
||||
const callbackPath = useBaseUrl('/stremio/addons/stremio-gdrive/callback');
|
||||
React.useEffect(() => {
|
||||
// set the redirect url to the current page
|
||||
setRedirectUrl(window.location.origin + callbackPath);
|
||||
console.log("Redirect URL: ", redirectUrl);
|
||||
// load the stored values from session storage if they exist
|
||||
|
||||
const storedClientId = sessionStorage.getItem(sessionKeys.clientId);
|
||||
const storedClientSecret = sessionStorage.getItem(sessionKeys.clientSecret);
|
||||
const storedAuthorisationCode = sessionStorage.getItem(sessionKeys.authorisationCode);
|
||||
const storedOauthError = sessionStorage.getItem(sessionKeys.oauthError);
|
||||
if (storedOauthError) {
|
||||
showToast(`Google OAuth Error: ${storedOauthError}`, "error");
|
||||
sessionStorage.removeItem(sessionKeys.oauthError);
|
||||
}
|
||||
if (storedClientId) {
|
||||
setClientId(storedClientId);
|
||||
}
|
||||
if (storedClientSecret) {
|
||||
setClientSecret(storedClientSecret);
|
||||
}
|
||||
if (storedAuthorisationCode) {
|
||||
setAuthorisationCode(storedAuthorisationCode);
|
||||
sessionStorage.removeItem(sessionKeys.authorisationCode);
|
||||
showToast("Authorisation code obtained successfully. Click `Get Credentials` to complete the process", "success");
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleClientIdChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value;
|
||||
setClientId(value);
|
||||
sessionStorage.setItem(sessionKeys.clientId, value);
|
||||
// reset the authorisation code since its no longer valid with different client id
|
||||
setAuthorisationCode("");
|
||||
sessionStorage.removeItem(sessionKeys.authorisationCode);
|
||||
};
|
||||
|
||||
const handleClientSecretChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value;
|
||||
setClientSecret(value);
|
||||
sessionStorage.setItem(sessionKeys.clientSecret, value);
|
||||
// reset the authorisation code since its no longer valid with different client secret
|
||||
setAuthorisationCode("");
|
||||
sessionStorage.removeItem(sessionKeys.authorisationCode);
|
||||
};
|
||||
|
||||
const getRefreshCode = () => {
|
||||
if (!clientId || clientId.length === 0) {
|
||||
showToast("Client ID is required", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clientSecret || clientSecret.length === 0) {
|
||||
showToast("Client Secret is required", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!authorisationCode || authorisationCode.length === 0) {
|
||||
showToast("Authorisation Code is required", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
const data = new URLSearchParams({
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
code: authorisationCode,
|
||||
grant_type: "authorization_code",
|
||||
redirect_uri: redirectUrl,
|
||||
scope: ''
|
||||
});
|
||||
|
||||
console.log("Requesting refresh token with data: ", data.toString());
|
||||
const response = fetch(tokenUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
},
|
||||
body: data.toString()
|
||||
});
|
||||
|
||||
response.then(handleResponse);
|
||||
}
|
||||
|
||||
const handleResponse = (res: Response) => {
|
||||
|
||||
if (res.ok) {
|
||||
res.json().then((data) => {
|
||||
console.log(data);
|
||||
// get the refresh token by making a request to the token url
|
||||
if (!data.refresh_token) {
|
||||
showToast("An unexpected error occurred", "error");
|
||||
return;
|
||||
}
|
||||
showToast("Refresh token obtained successfully. Copy the code and paste it into your script.", "success");
|
||||
// reset the authorisation code as it can no longer be used
|
||||
setAuthorisationCode("");
|
||||
sessionStorage.removeItem(sessionKeys.authorisationCode);
|
||||
setRefreshToken(data.refresh_token);
|
||||
});
|
||||
} else {
|
||||
let text = res.text();
|
||||
text.then((data) => {
|
||||
showToast(`Error getting the refresh token:${data}`, "error");
|
||||
});
|
||||
// reset any previously obtained refresh token
|
||||
setRefreshToken("");
|
||||
}
|
||||
};
|
||||
|
||||
const getCodeBlock = () => {
|
||||
return (
|
||||
<CodeBlock language="jsx">
|
||||
{
|
||||
`var credentials = {
|
||||
"client_id": "${clientId}",
|
||||
"client_secret": "${clientSecret}",
|
||||
"refresh_token": "${refreshToken}"
|
||||
};`
|
||||
}
|
||||
</CodeBlock>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<div className={styles.googleAuthFormContainer} >
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<label className={styles.googleAuthFormLabel} htmlFor="client-id">Client ID: </label>
|
||||
<input className={styles.googleAuthFormInput} id="client-id" type="text" placeholder="Client ID" value={clientId} onChange={handleClientIdChange} />
|
||||
</div>
|
||||
<div>
|
||||
<label className={styles.googleAuthFormLabel} htmlFor="client-secret">Client Secret: </label>
|
||||
<input className={styles.googleAuthFormInput} type="text" placeholder="Client Secret" value={clientSecret} onChange={handleClientSecretChange} />
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<button className={`${styles.googleAuthFormButton} default-themed-button`} onClick={() => openAuthorisationUrl(clientId, redirectUrl)}>Get Authorisation Code</button>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<label className={styles.googleAuthFormLabel} htmlFor="authorisation-code">Authorisation Code: </label>
|
||||
<input className={styles.googleAuthFormInput} type="text" placeholder="Authorisation Code" value={authorisationCode} onChange={(e) => setAuthorisationCode(e.target.value)} />
|
||||
</div>
|
||||
<div>
|
||||
<button className={`${styles.googleAuthFormButton} default-themed-button`} onClick={getRefreshCode}>Get Credentials</button>
|
||||
</div>
|
||||
<div>
|
||||
{refreshToken.length > 0 && getCodeBlock()}
|
||||
</div>
|
||||
<div>
|
||||
<p style={{"textAlign": "center", "fontWeight": "inherit", "marginTop": "20px"}}>
|
||||
View the <a href="https://github.com/Viren070/guides/blob/main/src/components/GoogleOAuth/index.tsx" target="_blank" rel="noreferrer">source code</a> for this tool
|
||||
</p>
|
||||
</div>
|
||||
</ div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
.googleAuthFormContainer {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
outline: 1px solid var(--ifm-color-primary);
|
||||
padding: 15px;
|
||||
border-radius: var(--ifm-global-radius);
|
||||
}
|
||||
|
||||
.googleAuthFormContainer > * {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.googleAuthFormLabel {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.googleAuthFormInput {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid var(--ifm-color-primary);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.googleAuthFormInput:focus {
|
||||
outline: none;
|
||||
border-color: var(--ifm-color-primary-darkest);
|
||||
box-shadow: 0 0 5px var(--ifm-color-primary-darkest);
|
||||
}
|
||||
|
||||
|
||||
.googleAuthFormButton {
|
||||
width: 100%;
|
||||
size: 16px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.googleAuthFormButton:hover {
|
||||
transform: scale(1.025);
|
||||
box-shadow: 0 0 5px var(--ifm-color-primary-darkest);
|
||||
border-color: 0 0 5px var(--ifm-color-primary-darkest);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ export const showToast = (message: string, type: 'info' | 'error' | 'success', t
|
||||
toast(message, {
|
||||
type: type,
|
||||
toastId: toastId,
|
||||
className: styles.customToast,
|
||||
className: styles.wideToast,
|
||||
});
|
||||
};
|
||||
|
||||
export function MyToastContainer(): JSX.Element {
|
||||
export function WideToastContainer(): JSX.Element {
|
||||
return (
|
||||
<ToastContainer
|
||||
stacked
|
||||
@@ -20,10 +20,9 @@ export function MyToastContainer(): JSX.Element {
|
||||
draggablePercent={60}
|
||||
draggableDirection='y'
|
||||
draggable="touch"
|
||||
limit={10}
|
||||
theme="colored"
|
||||
transition={Slide}
|
||||
className={styles.customContainer}
|
||||
className={styles.wideToastContainer}
|
||||
closeButton={false}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
}
|
||||
|
||||
|
||||
.customContainer {
|
||||
.wideToastContainer {
|
||||
width: auto; /* Ensure the width is auto to respect max-width */
|
||||
padding: 10px; /* Add padding to ensure space on all sides */
|
||||
left: 5vw; /* Add space on the left */
|
||||
@@ -29,14 +29,14 @@
|
||||
transform: none; /* Remove transform */
|
||||
border-radius: var(--ifm-global-radius); /* Ensure the border-radius is respected */
|
||||
}
|
||||
.customToast {
|
||||
.wideToast {
|
||||
border-radius: var(--ifm-global-radius); /* Ensure the border-radius is respected */
|
||||
justify-content: center; /* Center content horizontally */
|
||||
box-shadow: var(--box-shadow); /* Use CSS variable */
|
||||
}
|
||||
|
||||
@media only screen and (max-width : 480px) {
|
||||
.customContainer {
|
||||
.wideToastContainer {
|
||||
width: auto; /* Ensure the width is auto to respect max-width */
|
||||
padding: 10px; /* Add padding to ensure space on all sides */
|
||||
left: 5vw; /* Add space on the left */
|
||||
@@ -45,7 +45,7 @@
|
||||
transform: none; /* Remove transform */
|
||||
border-radius: var(--ifm-global-radius); /* Ensure the border-radius is respected */
|
||||
}
|
||||
.customToast {
|
||||
.wideToast {
|
||||
border-radius: var(--ifm-global-radius); /* Ensure the border-radius is respected */
|
||||
justify-content: center; /* Center content horizontally */
|
||||
|
||||
|
||||
@@ -102,3 +102,35 @@
|
||||
.hidden-tabs {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.default-themed-button {
|
||||
background: var(--ifm-color-primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 15px;
|
||||
cursor: pointer;
|
||||
border-radius: var(--ifm-global-radius);
|
||||
margin: 0 auto;
|
||||
box-shadow: var(--button-box-shadow);
|
||||
transition: transform 0.2s, box-shadow 0.2s, background 0.2s;
|
||||
}
|
||||
|
||||
.default-themed-button:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.default-themed-button:disabled:hover {
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.default-themed-button:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: var(--button-hover-box-shadow);
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import Footer from '@theme-original/Footer';
|
||||
import { MyToastContainer } from '@site/src/components/Toasts';
|
||||
import { WideToastContainer } from '@site/src/components/Toasts';
|
||||
|
||||
export default function FooterWrapper(props) {
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<MyToastContainer />
|
||||
<WideToastContainer />
|
||||
</section>
|
||||
<Footer {...props} />
|
||||
</>
|
||||
|
||||
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 44 KiB |