mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Display leaderboard limit in leaderboard admin for free accounts (#4497)
This commit is contained in:
@@ -28,7 +28,7 @@ import { formatScore } from '../../Leaderboard/LeaderboardScoreFormatter';
|
||||
type Props = {|
|
||||
entries: ?Array<LeaderboardDisplayData>,
|
||||
customizationSettings: ?LeaderboardCustomizationSettings,
|
||||
onDeleteEntry: (entryId: string) => Promise<void>,
|
||||
onDeleteEntry: (entry: LeaderboardDisplayData) => Promise<void>,
|
||||
isLoading: boolean,
|
||||
erroredEntry?: {| entryId: string, message: React.Node |},
|
||||
navigation: {|
|
||||
@@ -108,7 +108,7 @@ const LeaderboardEntriesTable = ({
|
||||
<Line>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => onDeleteEntry(entry.id)}
|
||||
onClick={() => onDeleteEntry(entry)}
|
||||
disabled={isLoading}
|
||||
tooltip={t`Remove entry`}
|
||||
>
|
||||
|
@@ -0,0 +1,71 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import Text from '../../UI/Text';
|
||||
import RaisedButton from '../../UI/RaisedButton';
|
||||
import AlertMessage from '../../UI/AlertMessage';
|
||||
import { Line, Column } from '../../UI/Grid';
|
||||
import { type Limits } from '../../Utils/GDevelopServices/Usage';
|
||||
|
||||
type Props = {|
|
||||
onUpgrade: () => void,
|
||||
onClose: () => void,
|
||||
limits: Limits,
|
||||
|};
|
||||
|
||||
const MaxLeaderboardCountAlertMessage = ({
|
||||
onUpgrade,
|
||||
onClose,
|
||||
limits,
|
||||
}: Props) => {
|
||||
const leaderboardLimits = limits.capabilities.leaderboards;
|
||||
if (!leaderboardLimits) return null;
|
||||
|
||||
return (
|
||||
<Line>
|
||||
<Column expand>
|
||||
<AlertMessage
|
||||
kind="warning"
|
||||
onHide={onClose}
|
||||
renderRightButton={
|
||||
leaderboardLimits.canMaximumCountPerGameBeIncreased
|
||||
? () => (
|
||||
<RaisedButton
|
||||
primary
|
||||
label={<Trans>Check our premiums plans</Trans>}
|
||||
onClick={onUpgrade}
|
||||
/>
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Text size="block-title">
|
||||
<Trans>
|
||||
You've reached your maximum of{' '}
|
||||
{leaderboardLimits.maximumCountPerGame} leaderboards for your game
|
||||
</Trans>
|
||||
</Text>
|
||||
<Text>
|
||||
{leaderboardLimits.canMaximumCountPerGameBeIncreased ? (
|
||||
<Trans>
|
||||
Update to GDevelop Premium to get more leaderboards, storage,
|
||||
and one-click packagings!
|
||||
</Trans>
|
||||
) : (
|
||||
// This should not happen at the moment since leaderboards are unlimited
|
||||
// in any paid plans but it could happen in the future with a plan that
|
||||
// cannot be increased and that has a max number of leaderboards.
|
||||
<Trans>
|
||||
To keep using GDevelop leaderboards, consider deleting old,
|
||||
unused leaderboards.
|
||||
</Trans>
|
||||
)}
|
||||
</Text>
|
||||
</AlertMessage>
|
||||
</Column>
|
||||
</Line>
|
||||
);
|
||||
};
|
||||
|
||||
export default MaxLeaderboardCountAlertMessage;
|
@@ -49,11 +49,11 @@ import {
|
||||
type Leaderboard,
|
||||
type LeaderboardCustomizationSettings,
|
||||
type LeaderboardUpdatePayload,
|
||||
type LeaderboardDisplayData,
|
||||
shortenUuidForDisplay,
|
||||
} from '../../Utils/GDevelopServices/Play';
|
||||
import LeaderboardContext from '../../Leaderboard/LeaderboardContext';
|
||||
import LeaderboardProvider from '../../Leaderboard/LeaderboardProvider';
|
||||
import Window from '../../Utils/Window';
|
||||
import LeaderboardEntriesTable from './LeaderboardEntriesTable';
|
||||
import { ResponsiveLineStackLayout } from '../../UI/Layout';
|
||||
import { useResponsiveWindowWidth } from '../../UI/Reponsive/ResponsiveWindowMeasurer';
|
||||
@@ -68,6 +68,10 @@ import { type LeaderboardSortOption } from '../../Utils/GDevelopServices/Play';
|
||||
import { formatScore } from '../../Leaderboard/LeaderboardScoreFormatter';
|
||||
import Toggle from '../../UI/Toggle';
|
||||
import GDevelopThemeContext from '../../UI/Theme/ThemeContext';
|
||||
import AuthenticatedUserContext from '../../Profile/AuthenticatedUserContext';
|
||||
import SubscriptionDialog from '../../Profile/SubscriptionDialog';
|
||||
import MaxLeaderboardCountAlertMessage from './MaxLeaderboardCountAlertMessage';
|
||||
import useAlertDialog from '../../UI/Alert/useAlertDialog';
|
||||
|
||||
type Props = {|
|
||||
onLoading: boolean => void,
|
||||
@@ -189,6 +193,18 @@ export const LeaderboardAdmin = ({
|
||||
const [isEditingAppearance, setIsEditingAppearance] = React.useState<boolean>(
|
||||
false
|
||||
);
|
||||
const { showConfirmation, showDeleteConfirmation } = useAlertDialog();
|
||||
const [
|
||||
displayMaxLeaderboardCountReachedWarning,
|
||||
setDisplayMaxLeaderboardCountReachedWarning,
|
||||
] = React.useState<boolean>(false);
|
||||
const [
|
||||
subscriptionDialogOpen,
|
||||
setSubscriptionDialogOpen,
|
||||
] = React.useState<boolean>(false);
|
||||
const authenticatedUser = React.useContext(AuthenticatedUserContext);
|
||||
const { limits } = authenticatedUser;
|
||||
|
||||
const [
|
||||
isEditingSortOptions,
|
||||
setIsEditingSortOptions,
|
||||
@@ -312,6 +328,18 @@ export const LeaderboardAdmin = ({
|
||||
setIsLoading(true);
|
||||
setApiError(null);
|
||||
try {
|
||||
if (limits && leaderboards) {
|
||||
const leaderboardLimits = limits.capabilities.leaderboards;
|
||||
if (
|
||||
leaderboardLimits &&
|
||||
leaderboardLimits.maximumCountPerGame > 0 &&
|
||||
leaderboards.length >= leaderboardLimits.maximumCountPerGame
|
||||
) {
|
||||
setDisplayMaxLeaderboardCountReachedWarning(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await createLeaderboard({
|
||||
name: 'New leaderboard',
|
||||
sort: 'ASC',
|
||||
@@ -333,11 +361,11 @@ export const LeaderboardAdmin = ({
|
||||
};
|
||||
|
||||
const onResetLeaderboard = async (i18n: I18nType) => {
|
||||
const answer = Window.showConfirmDialog(
|
||||
i18n._(
|
||||
t`All current entries will be deleted, are you sure you want to reset this leaderboard? This can't be undone.`
|
||||
)
|
||||
);
|
||||
if (!currentLeaderboard) return;
|
||||
const answer = await showConfirmation({
|
||||
title: t`Reset leaderboard ${currentLeaderboard.name}`,
|
||||
message: t`All current entries will be deleted, are you sure you want to reset this leaderboard? This can't be undone.`,
|
||||
});
|
||||
if (!answer) return;
|
||||
|
||||
setIsLoading(true);
|
||||
@@ -367,11 +395,13 @@ export const LeaderboardAdmin = ({
|
||||
};
|
||||
|
||||
const onDeleteLeaderboard = async (i18n: I18nType) => {
|
||||
const answer = Window.showConfirmDialog(
|
||||
i18n._(
|
||||
t`Are you sure you want to delete this leaderboard and all of its entries? This can't be undone.`
|
||||
)
|
||||
);
|
||||
if (!currentLeaderboard) return;
|
||||
const answer = await showDeleteConfirmation({
|
||||
title: t`Delete leaderboard ${currentLeaderboard.name}`,
|
||||
message: t`Are you sure you want to delete this leaderboard and all of its entries? This can't be undone.`,
|
||||
confirmText: currentLeaderboard.name,
|
||||
fieldMessage: t`Type the name of the leaderboard:`,
|
||||
});
|
||||
if (!answer) return;
|
||||
|
||||
setIsLoading(true);
|
||||
@@ -394,18 +424,21 @@ export const LeaderboardAdmin = ({
|
||||
}
|
||||
};
|
||||
|
||||
const onDeleteEntry = async (i18n: I18nType, entryId: string) => {
|
||||
const answer = Window.showConfirmDialog(
|
||||
i18n._(
|
||||
t`Are you sure you want to delete this entry? This can't be undone.`
|
||||
)
|
||||
);
|
||||
const onDeleteEntry = async (
|
||||
i18n: I18nType,
|
||||
entry: LeaderboardDisplayData
|
||||
) => {
|
||||
if (!currentLeaderboard) return;
|
||||
const answer = await showConfirmation({
|
||||
title: t`Delete score ${entry.score} from ${entry.playerName}`,
|
||||
message: t`Are you sure you want to delete this entry? This can't be undone.`,
|
||||
});
|
||||
if (!answer) return;
|
||||
|
||||
setIsLoading(true);
|
||||
setApiError(null);
|
||||
try {
|
||||
await deleteLeaderboardEntry(entryId);
|
||||
await deleteLeaderboardEntry(entry.id);
|
||||
} catch (err) {
|
||||
console.error('An error occurred when deleting entry', err);
|
||||
setApiError({
|
||||
@@ -415,7 +448,7 @@ export const LeaderboardAdmin = ({
|
||||
An error occurred when deleting the entry, please try again.
|
||||
</Trans>
|
||||
),
|
||||
itemId: entryId,
|
||||
itemId: entry.id,
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@@ -839,183 +872,201 @@ export const LeaderboardAdmin = ({
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<>
|
||||
<ResponsiveLineStackLayout noMargin expand noColumnMargin>
|
||||
<div style={styles.leftColumn}>
|
||||
<Paper
|
||||
elevation={5}
|
||||
style={{
|
||||
...styles.leaderboardConfigurationPaper,
|
||||
backgroundColor: gdevelopTheme.palette.alternateCanvasColor,
|
||||
}}
|
||||
>
|
||||
<Column>
|
||||
<Line>
|
||||
{currentLeaderboard && leaderboards ? (
|
||||
<SelectField
|
||||
fullWidth
|
||||
floatingLabelText={<Trans>Leaderboard name</Trans>}
|
||||
value={currentLeaderboard.id}
|
||||
onChange={(e, i, leaderboardId) => {
|
||||
selectLeaderboard(leaderboardId);
|
||||
}}
|
||||
<Column noMargin expand>
|
||||
{displayMaxLeaderboardCountReachedWarning && limits && (
|
||||
<MaxLeaderboardCountAlertMessage
|
||||
onUpgrade={() => setSubscriptionDialogOpen(true)}
|
||||
onClose={() =>
|
||||
setDisplayMaxLeaderboardCountReachedWarning(false)
|
||||
}
|
||||
limits={limits}
|
||||
/>
|
||||
)}
|
||||
<ResponsiveLineStackLayout noMargin expand noColumnMargin>
|
||||
<div style={styles.leftColumn}>
|
||||
<Paper
|
||||
elevation={5}
|
||||
style={{
|
||||
...styles.leaderboardConfigurationPaper,
|
||||
backgroundColor: gdevelopTheme.palette.alternateCanvasColor,
|
||||
}}
|
||||
>
|
||||
<Column>
|
||||
<Line noMargin>
|
||||
{currentLeaderboard && leaderboards ? (
|
||||
<SelectField
|
||||
fullWidth
|
||||
floatingLabelText={<Trans>Leaderboard name</Trans>}
|
||||
value={currentLeaderboard.id}
|
||||
onChange={(e, i, leaderboardId) => {
|
||||
selectLeaderboard(leaderboardId);
|
||||
}}
|
||||
>
|
||||
{leaderboards.map(leaderboard => (
|
||||
<SelectOption
|
||||
key={leaderboard.id}
|
||||
value={leaderboard.id}
|
||||
primaryText={
|
||||
leaderboard.primary
|
||||
? t`${leaderboard.name} (default)`
|
||||
: leaderboard.name
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</SelectField>
|
||||
) : null}
|
||||
<IconButton
|
||||
onClick={onCreateLeaderboard}
|
||||
disabled={isEditingName || isRequestPending}
|
||||
>
|
||||
{leaderboards.map(leaderboard => (
|
||||
<SelectOption
|
||||
key={leaderboard.id}
|
||||
value={leaderboard.id}
|
||||
primaryText={
|
||||
leaderboard.primary
|
||||
? t`${leaderboard.name} (default)`
|
||||
: leaderboard.name
|
||||
<Add />
|
||||
</IconButton>
|
||||
</Line>
|
||||
{currentLeaderboard ? (
|
||||
<>
|
||||
<List>
|
||||
{getLeaderboardDescription(
|
||||
i18n,
|
||||
currentLeaderboard
|
||||
).map((item, index) => (
|
||||
<React.Fragment key={`fragment-${item.key}`}>
|
||||
{index > 0 ? (
|
||||
<Divider
|
||||
key={`divider-${item.key}`}
|
||||
component="li"
|
||||
/>
|
||||
) : null}
|
||||
<ListItem key={item.key} disableGutters>
|
||||
<ListItemAvatar>
|
||||
<Avatar>{item.avatar}</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
disableTypography
|
||||
secondary={item.secondaryText}
|
||||
>
|
||||
{item.text}
|
||||
</ListItemText>
|
||||
{item.secondaryAction ? (
|
||||
<ListItemSecondaryAction>
|
||||
{item.secondaryAction}
|
||||
</ListItemSecondaryAction>
|
||||
) : null}
|
||||
</ListItem>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</List>
|
||||
<Line justifyContent="space-between">
|
||||
<FlatButton
|
||||
leftIcon={<Delete />}
|
||||
label={<Trans>Delete</Trans>}
|
||||
disabled={isRequestPending || isEditingName}
|
||||
onClick={() => onDeleteLeaderboard(i18n)}
|
||||
/>
|
||||
<RaisedButton
|
||||
label={
|
||||
currentLeaderboard.primary ? (
|
||||
<Trans>Default</Trans>
|
||||
) : (
|
||||
<Trans>Set as default</Trans>
|
||||
)
|
||||
}
|
||||
disabled={
|
||||
isRequestPending ||
|
||||
isEditingName ||
|
||||
currentLeaderboard.primary
|
||||
}
|
||||
onClick={() =>
|
||||
onUpdateLeaderboard(i18n, { primary: true })
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</SelectField>
|
||||
</Line>
|
||||
{apiError &&
|
||||
(apiError.action === 'leaderboardDeletion' ||
|
||||
apiError.action === 'leaderboardPrimaryUpdate') ? (
|
||||
<PlaceholderError>
|
||||
{apiError.message}
|
||||
</PlaceholderError>
|
||||
) : null}
|
||||
</>
|
||||
) : null}
|
||||
<IconButton
|
||||
onClick={onCreateLeaderboard}
|
||||
disabled={isEditingName || isRequestPending}
|
||||
>
|
||||
<Add />
|
||||
</IconButton>
|
||||
</Line>
|
||||
{currentLeaderboard ? (
|
||||
<>
|
||||
<List>
|
||||
{getLeaderboardDescription(
|
||||
i18n,
|
||||
currentLeaderboard
|
||||
).map((item, index) => (
|
||||
<React.Fragment key={`fragment-${item.key}`}>
|
||||
{index > 0 ? (
|
||||
<Divider
|
||||
key={`divider-${item.key}`}
|
||||
component="li"
|
||||
/>
|
||||
) : null}
|
||||
<ListItem key={item.key} disableGutters>
|
||||
<ListItemAvatar>
|
||||
<Avatar>{item.avatar}</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
disableTypography
|
||||
secondary={item.secondaryText}
|
||||
>
|
||||
{item.text}
|
||||
</ListItemText>
|
||||
{item.secondaryAction ? (
|
||||
<ListItemSecondaryAction>
|
||||
{item.secondaryAction}
|
||||
</ListItemSecondaryAction>
|
||||
) : null}
|
||||
</ListItem>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</List>
|
||||
<Line justifyContent="space-between">
|
||||
<FlatButton
|
||||
leftIcon={<Delete />}
|
||||
label={<Trans>Delete</Trans>}
|
||||
disabled={isRequestPending || isEditingName}
|
||||
onClick={() => onDeleteLeaderboard(i18n)}
|
||||
/>
|
||||
<RaisedButton
|
||||
label={
|
||||
currentLeaderboard.primary ? (
|
||||
<Trans>Default</Trans>
|
||||
) : (
|
||||
<Trans>Set as default</Trans>
|
||||
)
|
||||
</Column>
|
||||
</Paper>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
...styles.rightColumn,
|
||||
paddingLeft: windowWidth === 'small' ? 0 : 20,
|
||||
}}
|
||||
>
|
||||
<Line alignItems="center" justifyContent="flex-end">
|
||||
<Toggle
|
||||
size="small"
|
||||
labelPosition="left"
|
||||
toggled={displayOnlyBestEntry}
|
||||
onToggle={(e, newValue) =>
|
||||
setDisplayOnlyBestEntry(newValue)
|
||||
}
|
||||
label={
|
||||
<Tooltip
|
||||
title={i18n._(
|
||||
t`When checked, will only display the best score of each player (only for the display below).`
|
||||
)}
|
||||
>
|
||||
<Text size="body2">
|
||||
<Trans>Player best entry</Trans>
|
||||
</Text>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<LargeSpacer />
|
||||
<Divider orientation="vertical" />
|
||||
<Spacer />
|
||||
<IconButton
|
||||
onClick={onFetchLeaderboardEntries}
|
||||
disabled={isRequestPending || isEditingName}
|
||||
tooltip={t`Refresh`}
|
||||
size="small"
|
||||
>
|
||||
<Refresh />
|
||||
</IconButton>
|
||||
<Spacer />
|
||||
</Line>
|
||||
{apiError && apiError.action === 'entriesFetching' ? (
|
||||
<CenteredError>
|
||||
<PlaceholderError onRetry={onFetchLeaderboardEntries}>
|
||||
{apiError.message}
|
||||
</PlaceholderError>
|
||||
</CenteredError>
|
||||
) : (
|
||||
<LeaderboardEntriesTable
|
||||
entries={entries}
|
||||
customizationSettings={
|
||||
currentLeaderboard
|
||||
? currentLeaderboard.customizationSettings
|
||||
: null
|
||||
}
|
||||
onDeleteEntry={entry => onDeleteEntry(i18n, entry)}
|
||||
isLoading={isRequestPending || isEditingName}
|
||||
navigation={{
|
||||
goToNextPage,
|
||||
goToPreviousPage,
|
||||
goToFirstPage,
|
||||
}}
|
||||
erroredEntry={
|
||||
apiError &&
|
||||
apiError.action === 'entryDeletion' &&
|
||||
apiError.itemId
|
||||
? {
|
||||
entryId: apiError.itemId,
|
||||
message: apiError.message,
|
||||
}
|
||||
disabled={
|
||||
isRequestPending ||
|
||||
isEditingName ||
|
||||
currentLeaderboard.primary
|
||||
}
|
||||
onClick={() =>
|
||||
onUpdateLeaderboard(i18n, { primary: true })
|
||||
}
|
||||
/>
|
||||
</Line>
|
||||
{apiError &&
|
||||
(apiError.action === 'leaderboardDeletion' ||
|
||||
apiError.action === 'leaderboardPrimaryUpdate') ? (
|
||||
<PlaceholderError>{apiError.message}</PlaceholderError>
|
||||
) : null}
|
||||
</>
|
||||
) : null}
|
||||
</Column>
|
||||
</Paper>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
...styles.rightColumn,
|
||||
paddingLeft: windowWidth === 'small' ? 0 : 20,
|
||||
}}
|
||||
>
|
||||
<Line alignItems="center" justifyContent="flex-end">
|
||||
<Toggle
|
||||
size="small"
|
||||
labelPosition="left"
|
||||
toggled={displayOnlyBestEntry}
|
||||
onToggle={(e, newValue) => setDisplayOnlyBestEntry(newValue)}
|
||||
label={
|
||||
<Tooltip
|
||||
title={i18n._(
|
||||
t`When checked, will only display the best score of each player (only for the display below).`
|
||||
)}
|
||||
>
|
||||
<Text size="body2">
|
||||
<Trans>Player best entry</Trans>
|
||||
</Text>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<LargeSpacer />
|
||||
<Divider orientation="vertical" />
|
||||
<Spacer />
|
||||
<IconButton
|
||||
onClick={onFetchLeaderboardEntries}
|
||||
disabled={isRequestPending || isEditingName}
|
||||
tooltip={t`Refresh`}
|
||||
size="small"
|
||||
>
|
||||
<Refresh />
|
||||
</IconButton>
|
||||
<Spacer />
|
||||
</Line>
|
||||
{apiError && apiError.action === 'entriesFetching' ? (
|
||||
<CenteredError>
|
||||
<PlaceholderError onRetry={onFetchLeaderboardEntries}>
|
||||
{apiError.message}
|
||||
</PlaceholderError>
|
||||
</CenteredError>
|
||||
) : (
|
||||
<LeaderboardEntriesTable
|
||||
entries={entries}
|
||||
customizationSettings={
|
||||
currentLeaderboard
|
||||
? currentLeaderboard.customizationSettings
|
||||
: null
|
||||
}
|
||||
onDeleteEntry={entryId => onDeleteEntry(i18n, entryId)}
|
||||
isLoading={isRequestPending || isEditingName}
|
||||
navigation={{
|
||||
goToNextPage,
|
||||
goToPreviousPage,
|
||||
goToFirstPage,
|
||||
}}
|
||||
erroredEntry={
|
||||
apiError &&
|
||||
apiError.action === 'entryDeletion' &&
|
||||
apiError.itemId
|
||||
? { entryId: apiError.itemId, message: apiError.message }
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</ResponsiveLineStackLayout>
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</ResponsiveLineStackLayout>
|
||||
</Column>
|
||||
{isEditingAppearance ? (
|
||||
<LeaderboardAppearanceDialog
|
||||
open
|
||||
@@ -1058,6 +1109,12 @@ export const LeaderboardAdmin = ({
|
||||
extremeAllowedScore={currentLeaderboard.extremeAllowedScore}
|
||||
/>
|
||||
) : null}
|
||||
{subscriptionDialogOpen && (
|
||||
<SubscriptionDialog
|
||||
open
|
||||
onClose={() => setSubscriptionDialogOpen(false)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</I18n>
|
||||
|
@@ -45,8 +45,8 @@ export const MaxProjectCountAlertMessage = ({ onUpgrade, limits }: Props) => {
|
||||
<Text>
|
||||
{canMaximumCountBeIncreased ? (
|
||||
<Trans>
|
||||
Update to GDevelop Premium to get more storage, one click
|
||||
packagings, and a shiny unicorn!
|
||||
Update to GDevelop Premium to get more storage, leaderboards,
|
||||
and one-click packagings!
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans>
|
||||
|
@@ -48,6 +48,14 @@ export type Capabilities = {
|
||||
maximumCount: number,
|
||||
canMaximumCountBeIncreased: boolean,
|
||||
},
|
||||
/**
|
||||
* leaderboards is marked as optional to prevent bugs at the moment
|
||||
* the limit is enforced (endpoint deployed after the new version is released)
|
||||
*/
|
||||
leaderboards?: {
|
||||
maximumCountPerGame: number,
|
||||
canMaximumCountPerGameBeIncreased: boolean,
|
||||
},
|
||||
};
|
||||
|
||||
export type CurrentUsages = {
|
||||
|
@@ -117,6 +117,10 @@ export const limitsForIndieUser: Limits = {
|
||||
maximumCount: 50,
|
||||
canMaximumCountBeIncreased: true,
|
||||
},
|
||||
leaderboards: {
|
||||
maximumCountPerGame: -1,
|
||||
canMaximumCountPerGameBeIncreased: false,
|
||||
},
|
||||
},
|
||||
limits: {
|
||||
'cordova-build': {
|
||||
@@ -141,6 +145,10 @@ export const limitsForProUser: Limits = {
|
||||
maximumCount: 100,
|
||||
canMaximumCountBeIncreased: false,
|
||||
},
|
||||
leaderboards: {
|
||||
maximumCountPerGame: -1,
|
||||
canMaximumCountPerGameBeIncreased: false,
|
||||
},
|
||||
},
|
||||
limits: {
|
||||
'cordova-build': {
|
||||
@@ -165,6 +173,10 @@ export const limitsReached: Limits = {
|
||||
maximumCount: 10,
|
||||
canMaximumCountBeIncreased: false,
|
||||
},
|
||||
leaderboards: {
|
||||
maximumCountPerGame: 3,
|
||||
canMaximumCountPerGameBeIncreased: true,
|
||||
},
|
||||
},
|
||||
limits: {
|
||||
'cordova-build': {
|
||||
|
@@ -18,13 +18,13 @@ export default {
|
||||
export const ForIndieUser = () => (
|
||||
<MaxProjectCountAlertMessage
|
||||
limits={limitsForIndieUser}
|
||||
onUpgrade={action('onUpgrade')}
|
||||
onUpgrade={() => action('onUpgrade')()}
|
||||
/>
|
||||
);
|
||||
|
||||
export const ForProUser = () => (
|
||||
<MaxProjectCountAlertMessage
|
||||
limits={limitsForProUser}
|
||||
onUpgrade={action('onUpgrade')}
|
||||
onUpgrade={() => action('onUpgrade')()}
|
||||
/>
|
||||
);
|
||||
|
@@ -10,7 +10,7 @@ import { type Leaderboard } from '../../../Utils/GDevelopServices/Play';
|
||||
import FixedHeightFlexContainer from '../../FixedHeightFlexContainer';
|
||||
|
||||
export default {
|
||||
title: 'LeaderboardAdmin',
|
||||
title: 'Leaderboard/LeaderboardAdmin',
|
||||
component: LeaderboardAdmin,
|
||||
decorators: [paperDecorator, muiDecorator],
|
||||
};
|
||||
|
@@ -7,7 +7,7 @@ import paperDecorator from '../../PaperDecorator';
|
||||
import LeaderboardAppearanceDialog from '../../../GameDashboard/LeaderboardAdmin/LeaderboardAppearanceDialog';
|
||||
|
||||
export default {
|
||||
title: 'LeaderboardAppearanceDialog ',
|
||||
title: 'Leaderboard/LeaderboardAppearanceDialog',
|
||||
component: LeaderboardAppearanceDialog,
|
||||
decorators: [paperDecorator, muiDecorator],
|
||||
};
|
||||
|
@@ -7,7 +7,7 @@ import paperDecorator from '../../PaperDecorator';
|
||||
import LeaderboardSortOptionsDialog from '../../../GameDashboard/LeaderboardAdmin/LeaderboardSortOptionsDialog';
|
||||
|
||||
export default {
|
||||
title: 'LeaderboardSortOptionsDialog ',
|
||||
title: 'Leaderboard/LeaderboardSortOptionsDialog',
|
||||
component: LeaderboardSortOptionsDialog,
|
||||
decorators: [paperDecorator, muiDecorator],
|
||||
};
|
||||
|
@@ -0,0 +1,21 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import muiDecorator from '../../ThemeDecorator';
|
||||
import paperDecorator from '../../PaperDecorator';
|
||||
import { limitsReached } from '../../../fixtures/GDevelopServicesTestData';
|
||||
import MaxLeaderboardCountAlertMessage from '../../../GameDashboard/LeaderboardAdmin/MaxLeaderboardCountAlertMessage';
|
||||
|
||||
export default {
|
||||
title: 'Leaderboard/MaxLeaderboardCountAlertMessage',
|
||||
component: MaxLeaderboardCountAlertMessage,
|
||||
decorators: [paperDecorator, muiDecorator],
|
||||
};
|
||||
|
||||
export const Default = () => (
|
||||
<MaxLeaderboardCountAlertMessage
|
||||
limits={limitsReached}
|
||||
onUpgrade={() => action('onUpgrade')()}
|
||||
onClose={() => action('onClose')()}
|
||||
/>
|
||||
);
|
Reference in New Issue
Block a user