mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Fix keyboard avoidance on touchscreens for Ask AI tab (#7670)
* Fix keyboard avoidance on touchscreens for Ask AI tab * Add placementId for in-app analytics Don't show in changelog
This commit is contained in:
@@ -30,6 +30,21 @@
|
||||
justify-content: center;
|
||||
|
||||
animation: new-chat-appear 0.5s;
|
||||
|
||||
margin-bottom: var(--safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.aiRequestChatContainer {
|
||||
display: flex;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
justify-content: stretch;
|
||||
flex: 1 1 0%;
|
||||
min-height: 0px;
|
||||
|
||||
margin-bottom: var(--safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.thinkingText {
|
||||
|
@@ -37,6 +37,7 @@ import Hammer from '../../UI/CustomSvgIcons/Hammer';
|
||||
import { ChatMessages } from './ChatMessages';
|
||||
import Send from '../../UI/CustomSvgIcons/Send';
|
||||
import { FeedbackBanner } from './FeedbackBanner';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const TOO_MANY_USER_MESSAGES_WARNING_COUNT = 5;
|
||||
const TOO_MANY_USER_MESSAGES_ERROR_COUNT = 10;
|
||||
@@ -330,6 +331,7 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
const subscriptionBanner =
|
||||
quota && quota.limitReached && increaseQuotaOffering !== 'none' ? (
|
||||
<GetSubscriptionCard
|
||||
placementId="ai-requests"
|
||||
subscriptionDialogOpeningReason={
|
||||
increaseQuotaOffering === 'subscribe'
|
||||
? 'AI requests (subscribe)'
|
||||
@@ -386,7 +388,13 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
|
||||
if (!aiRequest) {
|
||||
return (
|
||||
<div className={classes.newChatContainer}>
|
||||
<div
|
||||
className={classNames({
|
||||
[classes.newChatContainer]: true,
|
||||
// Move the entire screen up when the soft keyboard is open:
|
||||
'avoid-soft-keyboard': true,
|
||||
})}
|
||||
>
|
||||
<ColumnStackLayout justifyContent="center" expand>
|
||||
<Line noMargin justifyContent="center">
|
||||
<RobotIcon rotating size={40} />
|
||||
@@ -620,11 +628,10 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<Column
|
||||
expand
|
||||
alignItems="stretch"
|
||||
justifyContent="stretch"
|
||||
useFullHeight
|
||||
<div
|
||||
className={classNames({
|
||||
[classes.aiRequestChatContainer]: true,
|
||||
})}
|
||||
>
|
||||
<ScrollView ref={scrollViewRef} style={styles.chatScrollView}>
|
||||
<ChatMessages
|
||||
@@ -663,6 +670,10 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
userMessage: userRequestTextPerAiRequestId[aiRequestId] || '',
|
||||
});
|
||||
}}
|
||||
className={classNames({
|
||||
// Move the form up when the soft keyboard is open:
|
||||
'avoid-soft-keyboard': true,
|
||||
})}
|
||||
>
|
||||
<ColumnStackLayout
|
||||
justifyContent="stretch"
|
||||
@@ -798,7 +809,7 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
</Column>
|
||||
</ColumnStackLayout>
|
||||
</form>
|
||||
</Column>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@@ -656,6 +656,7 @@ const PrivateAssetPackInformationPage = ({
|
||||
analyticsMetadata: {
|
||||
reason: 'Claim asset pack',
|
||||
recommendedPlanId: 'gdevelop_gold',
|
||||
placementId: 'claim-asset-pack',
|
||||
},
|
||||
filter: 'individual',
|
||||
})
|
||||
|
@@ -174,6 +174,7 @@ const LockedCourseChapterPreview = React.forwardRef<Props, HTMLDivElement>(
|
||||
analyticsMetadata: {
|
||||
reason: 'Unlock course chapter',
|
||||
recommendedPlanId: 'gdevelop_silver',
|
||||
placementId: 'unlock-course-chapter',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@@ -371,6 +371,7 @@ export default class LocalPreviewLauncher extends React.Component<
|
||||
}
|
||||
id="Preview over wifi"
|
||||
title={<Trans>Preview over wifi</Trans>}
|
||||
placementId="preview-wifi"
|
||||
mode="try"
|
||||
isNotShownDuringInAppTutorial
|
||||
/>
|
||||
@@ -382,6 +383,7 @@ export default class LocalPreviewLauncher extends React.Component<
|
||||
title={
|
||||
<Trans>Live preview (apply changes to the running preview)</Trans>
|
||||
}
|
||||
placementId="hot-reloading"
|
||||
mode="try"
|
||||
isNotShownDuringInAppTutorial
|
||||
/>
|
||||
|
@@ -326,6 +326,7 @@ const InviteHome = ({ cloudProjectId }: Props) => {
|
||||
<GetSubscriptionCard
|
||||
subscriptionDialogOpeningReason="Add collaborators on project"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_startup"
|
||||
placementId="invite-collaborators"
|
||||
>
|
||||
<Text>
|
||||
<Trans>
|
||||
|
@@ -350,6 +350,7 @@ function LeaderboardAppearanceDialog({
|
||||
<GetSubscriptionCard
|
||||
subscriptionDialogOpeningReason="Leaderboard customization"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId="leaderboards-customization"
|
||||
>
|
||||
<Line>
|
||||
<Column noMargin>
|
||||
@@ -406,6 +407,7 @@ function LeaderboardAppearanceDialog({
|
||||
<GetSubscriptionCard
|
||||
subscriptionDialogOpeningReason="Leaderboard customization"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_startup"
|
||||
placementId="leaderboards-customization"
|
||||
>
|
||||
<Line>
|
||||
<Column noMargin>
|
||||
|
@@ -256,6 +256,7 @@ function LeaderboardOptionsDialog({
|
||||
<GetSubscriptionCard
|
||||
subscriptionDialogOpeningReason="Leaderboard customization"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_startup"
|
||||
placementId="leaderboards-customization"
|
||||
>
|
||||
<Line>
|
||||
<Column noMargin>
|
||||
|
@@ -47,6 +47,7 @@ const MaxLeaderboardCountAlertMessage = () => {
|
||||
<Column expand>
|
||||
<GetSubscriptionCard
|
||||
subscriptionDialogOpeningReason="Leaderboard count per game limit reached"
|
||||
placementId="leaderboards"
|
||||
label={
|
||||
!hasValidSubscription ? (
|
||||
<Trans>Upgrade to GDevelop Premium</Trans>
|
||||
|
@@ -98,6 +98,7 @@ const ServicesWidget = ({
|
||||
analyticsMetadata: {
|
||||
reason: 'Leaderboard count per game limit reached',
|
||||
recommendedPlanId: 'gdevelop_silver',
|
||||
placementId: 'leaderboards',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@@ -105,6 +105,7 @@ export class DebuggerEditorContainer extends React.Component<
|
||||
}
|
||||
id="Debugger"
|
||||
title={<Trans>Debugger</Trans>}
|
||||
placementId="debugger"
|
||||
mode="try"
|
||||
/>
|
||||
</React.Fragment>
|
||||
|
@@ -55,6 +55,7 @@ export const MaxProjectCountAlertMessage = ({ margin }: Props) => {
|
||||
}
|
||||
hideButton={!canMaximumCountBeIncreased}
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId="max-projects-reached"
|
||||
>
|
||||
<Line>
|
||||
<Column noMargin expand>
|
||||
|
@@ -241,6 +241,7 @@ const EducationMarketingSection = ({
|
||||
analyticsMetadata: {
|
||||
reason: 'Callout in Classroom tab',
|
||||
recommendedPlanId: 'gdevelop_education',
|
||||
placementId: 'education',
|
||||
},
|
||||
});
|
||||
},
|
||||
|
@@ -638,6 +638,7 @@ const ManageEducationAccountDialog = ({ onClose }: Props) => {
|
||||
analyticsMetadata: {
|
||||
reason: 'Manage subscription as teacher',
|
||||
recommendedPlanId: 'gdevelop_education',
|
||||
placementId: 'education',
|
||||
},
|
||||
filter: 'education',
|
||||
})
|
||||
|
@@ -173,6 +173,7 @@ const CurrentUsageDisplayer = ({
|
||||
}
|
||||
hideButton={cannotUpgradeSubscription}
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId="builds"
|
||||
>
|
||||
<Line>
|
||||
{!isFeatureLocked ? (
|
||||
@@ -219,6 +220,7 @@ const CurrentUsageDisplayer = ({
|
||||
}
|
||||
}
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId="builds"
|
||||
>
|
||||
<Line>
|
||||
{!isFeatureLocked ? (
|
||||
|
@@ -3,7 +3,10 @@ import * as React from 'react';
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Column, Line } from '../../UI/Grid';
|
||||
import { ResponsiveLineStackLayout } from '../../UI/Layout';
|
||||
import { type SubscriptionDialogDisplayReason } from '../../Utils/Analytics/EventSender';
|
||||
import {
|
||||
type SubscriptionDialogDisplayReason,
|
||||
type SubscriptionPlacementId,
|
||||
} from '../../Utils/Analytics/EventSender';
|
||||
import { SubscriptionSuggestionContext } from './SubscriptionSuggestionContext';
|
||||
import RaisedButton from '../../UI/RaisedButton';
|
||||
import FlatButton from '../../UI/FlatButton';
|
||||
@@ -53,6 +56,7 @@ type Props = {|
|
||||
| 'gdevelop_startup'
|
||||
| 'gdevelop_education',
|
||||
canHide?: boolean,
|
||||
placementId: SubscriptionPlacementId,
|
||||
|};
|
||||
|
||||
const GetSubscriptionCard = ({
|
||||
@@ -66,6 +70,7 @@ const GetSubscriptionCard = ({
|
||||
filter,
|
||||
recommendedPlanIdIfNoSubscription,
|
||||
canHide,
|
||||
placementId,
|
||||
}: Props) => {
|
||||
const [isHidden, setIsHidden] = React.useState(false);
|
||||
const { subscription } = React.useContext(AuthenticatedUserContext);
|
||||
@@ -116,6 +121,7 @@ const GetSubscriptionCard = ({
|
||||
analyticsMetadata: {
|
||||
reason: subscriptionDialogOpeningReason,
|
||||
recommendedPlanId: actualPlanIdToRecommend,
|
||||
placementId,
|
||||
},
|
||||
filter,
|
||||
});
|
||||
|
@@ -16,6 +16,7 @@ import { isNativeMobileApp } from '../../Utils/Platform';
|
||||
import InAppTutorialContext from '../../InAppTutorial/InAppTutorialContext';
|
||||
import GetSubscriptionCard from './GetSubscriptionCard';
|
||||
import { ColumnStackLayout } from '../../UI/Layout';
|
||||
import { type SubscriptionPlacementId } from '../../Utils/Analytics/EventSender';
|
||||
|
||||
export type SubscriptionCheckerInterface = {|
|
||||
checkUserHasSubscription: () => boolean,
|
||||
@@ -29,6 +30,7 @@ type Props = {|
|
||||
| 'Debugger'
|
||||
| 'Hot reloading'
|
||||
| 'Preview over wifi',
|
||||
placementId: SubscriptionPlacementId,
|
||||
onChangeSubscription?: () => Promise<void> | void,
|
||||
mode: 'try' | 'mandatory',
|
||||
isNotShownDuringInAppTutorial?: boolean,
|
||||
@@ -39,7 +41,14 @@ const SubscriptionChecker = React.forwardRef<
|
||||
SubscriptionCheckerInterface
|
||||
>(
|
||||
(
|
||||
{ mode, id, title, onChangeSubscription, isNotShownDuringInAppTutorial },
|
||||
{
|
||||
mode,
|
||||
id,
|
||||
title,
|
||||
onChangeSubscription,
|
||||
placementId,
|
||||
isNotShownDuringInAppTutorial,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const authenticatedUser = React.useContext(AuthenticatedUserContext);
|
||||
@@ -122,6 +131,7 @@ const SubscriptionChecker = React.forwardRef<
|
||||
setDialogOpen(false);
|
||||
}}
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId={placementId}
|
||||
>
|
||||
<Column noMargin expand>
|
||||
<Text>
|
||||
|
@@ -341,7 +341,10 @@ const SubscriptionDetails = ({
|
||||
primary
|
||||
onClick={() => {
|
||||
openSubscriptionDialog({
|
||||
analyticsMetadata: { reason: 'Consult profile' },
|
||||
analyticsMetadata: {
|
||||
reason: 'Consult profile',
|
||||
placementId: 'profile',
|
||||
},
|
||||
});
|
||||
}}
|
||||
disabled={isManageSubscriptionLoading}
|
||||
@@ -390,6 +393,7 @@ const SubscriptionDetails = ({
|
||||
<GetSubscriptionCard
|
||||
label={<Trans>Choose a subscription</Trans>}
|
||||
subscriptionDialogOpeningReason="Consult profile"
|
||||
placementId="profile"
|
||||
>
|
||||
<Text noMargin>
|
||||
<Trans>
|
||||
@@ -440,7 +444,10 @@ const SubscriptionDetails = ({
|
||||
color={buttonColor}
|
||||
onClick={() =>
|
||||
openSubscriptionDialog({
|
||||
analyticsMetadata: { reason: 'Consult profile' },
|
||||
analyticsMetadata: {
|
||||
reason: 'Consult profile',
|
||||
placementId: 'profile',
|
||||
},
|
||||
filter: key,
|
||||
})
|
||||
}
|
||||
@@ -458,6 +465,7 @@ const SubscriptionDetails = ({
|
||||
label={<Trans>Choose a subscription</Trans>}
|
||||
subscriptionDialogOpeningReason="Consult profile"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId="profile"
|
||||
>
|
||||
<Text noMargin>
|
||||
<Trans>
|
||||
|
@@ -5,6 +5,7 @@ import SubscriptionDialog from './SubscriptionDialog';
|
||||
import {
|
||||
sendSubscriptionDialogShown,
|
||||
type SubscriptionDialogDisplayReason,
|
||||
type SubscriptionPlacementId,
|
||||
} from '../../Utils/Analytics/EventSender';
|
||||
import { isNativeMobileApp } from '../../Utils/Platform';
|
||||
import {
|
||||
@@ -26,6 +27,7 @@ export type SubscriptionType = 'individual' | 'team' | 'education';
|
||||
export type SubscriptionAnalyticsMetadata = {|
|
||||
reason: SubscriptionDialogDisplayReason,
|
||||
recommendedPlanId?: string,
|
||||
placementId: SubscriptionPlacementId,
|
||||
preStep?: 'subscriptionChecker',
|
||||
|};
|
||||
|
||||
|
@@ -238,6 +238,7 @@ export const LoadingScreenEditor = ({
|
||||
<GetSubscriptionCard
|
||||
subscriptionDialogOpeningReason="Disable GDevelop splash at startup"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId="gdevelop-branding"
|
||||
>
|
||||
<Text>
|
||||
<Trans>
|
||||
@@ -555,6 +556,7 @@ export const LoadingScreenEditor = ({
|
||||
mode="mandatory"
|
||||
id="Disable GDevelop splash at startup"
|
||||
title={<Trans>Disable GDevelop splash at startup</Trans>}
|
||||
placementId="gdevelop-branding"
|
||||
/>
|
||||
</ColumnStackLayout>
|
||||
)}
|
||||
|
@@ -32,6 +32,7 @@ const GetPremiumButton = () => {
|
||||
analyticsMetadata: {
|
||||
reason: 'Account get premium',
|
||||
recommendedPlanId: 'gdevelop_silver',
|
||||
placementId: 'account-get-premium',
|
||||
},
|
||||
});
|
||||
}}
|
||||
|
@@ -390,6 +390,26 @@ export type SubscriptionDialogDisplayReason =
|
||||
| 'AI requests (subscribe)'
|
||||
| 'AI requests (upgrade)';
|
||||
|
||||
export type SubscriptionPlacementId =
|
||||
| 'builds'
|
||||
| 'debugger'
|
||||
| 'gdevelop-branding'
|
||||
| 'generate-from-prompt'
|
||||
| 'hot-reloading'
|
||||
| 'leaderboards-customization'
|
||||
| 'leaderboards'
|
||||
| 'max-projects-reached'
|
||||
| 'opening-from-link'
|
||||
| 'preview-wifi'
|
||||
| 'profile'
|
||||
| 'invite-collaborators'
|
||||
| 'version-history'
|
||||
| 'claim-asset-pack'
|
||||
| 'unlock-course-chapter'
|
||||
| 'account-get-premium'
|
||||
| 'education'
|
||||
| 'ai-requests';
|
||||
|
||||
export const sendSubscriptionDialogShown = (
|
||||
metadata: SubscriptionAnalyticsMetadata
|
||||
) => {
|
||||
|
@@ -43,6 +43,7 @@ const useOpenInitialDialog = ({
|
||||
analyticsMetadata: {
|
||||
reason: 'Landing dialog at opening',
|
||||
recommendedPlanId,
|
||||
placementId: 'opening-from-link',
|
||||
},
|
||||
});
|
||||
removeRouteArguments(['initial-dialog', 'recommended-plan-id']);
|
||||
|
@@ -455,6 +455,7 @@ const useVersionHistory = ({
|
||||
forceColumnLayout
|
||||
filter="team"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_startup"
|
||||
placementId="version-history"
|
||||
>
|
||||
<Text>
|
||||
<Trans>
|
||||
|
@@ -17,7 +17,10 @@ export default {
|
||||
|
||||
export const Default = () => (
|
||||
<AuthenticatedUserContext.Provider value={fakeNotAuthenticatedUser}>
|
||||
<GetSubscriptionCard subscriptionDialogOpeningReason="Build limit reached">
|
||||
<GetSubscriptionCard
|
||||
placementId="builds"
|
||||
subscriptionDialogOpeningReason="Build limit reached"
|
||||
>
|
||||
<Line>
|
||||
<Column noMargin>
|
||||
<Text noMargin>
|
||||
@@ -32,6 +35,7 @@ export const Default = () => (
|
||||
export const CustomLabel = () => (
|
||||
<AuthenticatedUserContext.Provider value={fakeNotAuthenticatedUser}>
|
||||
<GetSubscriptionCard
|
||||
placementId="builds"
|
||||
subscriptionDialogOpeningReason="Build limit reached"
|
||||
label="Upgrade your subscription"
|
||||
>
|
||||
@@ -49,6 +53,7 @@ export const CustomLabel = () => (
|
||||
export const ButtonHidden = () => (
|
||||
<AuthenticatedUserContext.Provider value={fakeNotAuthenticatedUser}>
|
||||
<GetSubscriptionCard
|
||||
placementId="builds"
|
||||
subscriptionDialogOpeningReason="Build limit reached"
|
||||
hideButton
|
||||
>
|
||||
@@ -66,6 +71,7 @@ export const ButtonHidden = () => (
|
||||
export const PayWithCreditsOptions = () => (
|
||||
<AuthenticatedUserContext.Provider value={fakeNotAuthenticatedUser}>
|
||||
<GetSubscriptionCard
|
||||
placementId="builds"
|
||||
subscriptionDialogOpeningReason="Build limit reached"
|
||||
payWithCreditsOptions={{
|
||||
label: 'Purchase with 100 credits',
|
||||
@@ -86,6 +92,7 @@ export const PayWithCreditsOptions = () => (
|
||||
export const ForceColumnLayout = () => (
|
||||
<AuthenticatedUserContext.Provider value={fakeNotAuthenticatedUser}>
|
||||
<GetSubscriptionCard
|
||||
placementId="builds"
|
||||
subscriptionDialogOpeningReason="Build limit reached"
|
||||
forceColumnLayout
|
||||
>
|
||||
|
@@ -38,6 +38,7 @@ export const NotAuthenticatedTryMode = () => {
|
||||
id="Preview over wifi"
|
||||
onChangeSubscription={action('change subscription')}
|
||||
mode="try"
|
||||
placementId="gdevelop-branding"
|
||||
/>
|
||||
</AuthenticatedUserContext.Provider>
|
||||
);
|
||||
@@ -59,6 +60,7 @@ export const NotAuthenticatedMandatoryMode = () => {
|
||||
id="Preview over wifi"
|
||||
onChangeSubscription={action('change subscription')}
|
||||
mode="mandatory"
|
||||
placementId="gdevelop-branding"
|
||||
/>
|
||||
</AuthenticatedUserContext.Provider>
|
||||
);
|
||||
@@ -82,6 +84,7 @@ export const UserWithNoSubscription = () => {
|
||||
id="Preview over wifi"
|
||||
onChangeSubscription={action('change subscription')}
|
||||
mode="mandatory"
|
||||
placementId="gdevelop-branding"
|
||||
/>
|
||||
</AuthenticatedUserContext.Provider>
|
||||
);
|
||||
@@ -103,6 +106,7 @@ export const UserWithGoldSubscription = () => {
|
||||
id="Preview over wifi"
|
||||
onChangeSubscription={action('change subscription')}
|
||||
mode="mandatory"
|
||||
placementId="gdevelop-branding"
|
||||
/>
|
||||
</AuthenticatedUserContext.Provider>
|
||||
);
|
||||
|
@@ -32,6 +32,7 @@ const SubscriptionDialogTestOpener = ({ label }: {| label: string |}) => {
|
||||
analyticsMetadata: {
|
||||
reason: 'Cloud Project limit reached',
|
||||
recommendedPlanId: 'gdevelop_silver',
|
||||
placementId: 'max-projects-reached',
|
||||
},
|
||||
});
|
||||
},
|
||||
|
Reference in New Issue
Block a user