render video detail inputs conditionally, terminateUpload on save
working, needs testing wip: sync styles for inputs, remove notes/tags for now wip: use single screen and switch on mode need to rebase working, needs testing wip: sync styles for inputs, remove notes/tags for now wip: use single screen and switch on mode need to rebase working, needs testing add game type and table size to terminateUpload
This commit is contained in:
parent
b05e354459
commit
b04c6aa345
2
app.json
2
app.json
@ -38,7 +38,7 @@
|
||||
"googleServicesFile": "./google-services.json"
|
||||
},
|
||||
"web": {
|
||||
"favicon": "./src/assets/favicon.png"
|
||||
"favicon": "./src/assets/icons/favicon.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@
|
||||
"graphql": "^16.8.1",
|
||||
"jest": "^29.2.1",
|
||||
"jest-expo": "~49.0.0",
|
||||
"railbird-gql": "git+https://dev.railbird.ai/railbird/railbird-gql.git#master",
|
||||
"railbird-gql": "git+https://dev.railbird.ai/railbird/railbird-gql.git#234d4d0fa90342f8af655c9ddf476f033caa322d",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.72.6",
|
||||
"react-native-dotenv": "^3.4.9",
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@ -4,7 +4,6 @@ import * as gql from "railbird-gql";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Button, StyleSheet, Text, View } from "react-native";
|
||||
import * as RNFS from "react-native-fs";
|
||||
|
||||
import {
|
||||
Camera,
|
||||
CameraRuntimeError,
|
||||
@ -19,6 +18,7 @@ import { RecordingButton } from "./capture-button";
|
||||
import { useIsForeground } from "./is-foreground";
|
||||
|
||||
type Dictionary<KeyType extends string | number | symbol, ValueType> = {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
[key in KeyType]: ValueType;
|
||||
};
|
||||
|
||||
@ -111,6 +111,7 @@ export default function CameraScreen({
|
||||
navigation,
|
||||
}): React.ReactElement {
|
||||
const apolloClient = useApolloClient();
|
||||
const { params } = route;
|
||||
const [createUpload, { data, loading, error }] =
|
||||
gql.useCreateUploadStreamMutation();
|
||||
|
||||
@ -127,6 +128,7 @@ export default function CameraScreen({
|
||||
console.log(`VideoId: ${newVideoId}`);
|
||||
setUploadManager(new StreamUploadManager(apolloClient, newVideoId));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data, uploadManager]);
|
||||
|
||||
const camera = useRef<Camera>(null);
|
||||
@ -143,14 +145,24 @@ export default function CameraScreen({
|
||||
}, []);
|
||||
|
||||
const onInitialized = useCallback(() => {
|
||||
console.log("Camera initialized!");
|
||||
setIsCameraInitialized(true);
|
||||
createUpload({ variables: { videoName: "Test" } });
|
||||
createUpload({
|
||||
variables: { videoName: params?.sessionName ?? "New session" },
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const onMediaCaptured = useCallback((media: PhotoFile | VideoFile) => {
|
||||
console.log(`Media captured! ${JSON.stringify(media)}`);
|
||||
}, []);
|
||||
const onMediaCaptured = useCallback(
|
||||
(media: PhotoFile | VideoFile) => {
|
||||
console.log(`Media captured! ${JSON.stringify(media)}`);
|
||||
navigation.push("SaveVideo", {
|
||||
videoId: uploadManager?.videoId,
|
||||
...params,
|
||||
});
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[uploadManager, navigation, params],
|
||||
);
|
||||
|
||||
const onVideoChunkReady = useCallback(
|
||||
(event) => {
|
||||
@ -179,7 +191,6 @@ export default function CameraScreen({
|
||||
|
||||
// Replace with error handling
|
||||
if (device === null) {
|
||||
console.log(device);
|
||||
// hasPermission redundant here - user should not be able to launch camera without permissions
|
||||
return (
|
||||
<Text>
|
||||
|
@ -8,6 +8,10 @@ interface PermissionMessage {
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
interface ApiError {
|
||||
title: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export const CAMERA_PERMISSION_DENIED: PermissionMessage = {
|
||||
android: {
|
||||
@ -19,3 +23,8 @@ export const CAMERA_PERMISSION_DENIED: PermissionMessage = {
|
||||
message: "Please go to Settings > Railbird > Camera and grant permissions.",
|
||||
},
|
||||
};
|
||||
|
||||
export const TERMINATE_UPLOAD_ERROR: ApiError = {
|
||||
title: "There was an issue.",
|
||||
message: "Please try again",
|
||||
};
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { Alert, Platform } from "react-native";
|
||||
import { CAMERA_PERMISSION_DENIED } from "./constants";
|
||||
import { CAMERA_PERMISSION_DENIED, TERMINATE_UPLOAD_ERROR } from "./constants";
|
||||
|
||||
const ALERT_TYPE = {
|
||||
camera: CAMERA_PERMISSION_DENIED,
|
||||
terminateUpload: TERMINATE_UPLOAD_ERROR,
|
||||
};
|
||||
|
||||
export const showAlert = (alertType: string) => {
|
||||
const alert = ALERT_TYPE[alertType];
|
||||
const { title, message } = alert[Platform.OS];
|
||||
const { title, message } = alert[Platform.OS] ?? alert;
|
||||
Alert.alert(title, message);
|
||||
};
|
||||
|
@ -4,10 +4,10 @@ import { Image } from "react-native";
|
||||
import CameraScreen from "../component/video/camera";
|
||||
import Profile from "../screens/profile";
|
||||
import Session from "../screens/session";
|
||||
import RecordScreen from "../screens/video-stack/record";
|
||||
import VideoDetails from "../screens/video-stack/video-details";
|
||||
import { tabIconColors } from "../styles";
|
||||
|
||||
import Icon from "../assets/favicon.png";
|
||||
import Icon from "../assets/icons/favicon.png";
|
||||
|
||||
const Tab = createBottomTabNavigator();
|
||||
const RecordStack = createNativeStackNavigator();
|
||||
@ -22,8 +22,17 @@ const tabIcons = {
|
||||
function VideoTabStack() {
|
||||
return (
|
||||
<RecordStack.Navigator screenOptions={{ headerShown: false }}>
|
||||
<RecordStack.Screen name="Record" component={RecordScreen} />
|
||||
<RecordStack.Screen
|
||||
name="Record"
|
||||
component={VideoDetails}
|
||||
initialParams={{ mode: "start-video" }}
|
||||
/>
|
||||
<RecordStack.Screen name="Camera" component={CameraScreen} />
|
||||
<RecordStack.Screen
|
||||
name="SaveVideo"
|
||||
component={VideoDetails}
|
||||
initialParams={{ mode: "save-video" }}
|
||||
/>
|
||||
</RecordStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
@ -1,161 +0,0 @@
|
||||
import React, { useCallback, useState } from "react";
|
||||
import {
|
||||
Keyboard,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
} from "react-native";
|
||||
import DropDownPicker from "react-native-dropdown-picker";
|
||||
// @ts-ignore
|
||||
import { useCameraPermission } from "react-native-vision-camera";
|
||||
import { showAlert } from "../../lib/alert-messages";
|
||||
import { recordStyles as styles } from "./styles";
|
||||
|
||||
interface CameraScreenParams {
|
||||
gameType: string;
|
||||
tableSize: string;
|
||||
tags: Array<string>;
|
||||
location: string;
|
||||
}
|
||||
|
||||
export default function RecordScreen({ navigation }): React.JSX.Element {
|
||||
// Permissions
|
||||
const { hasPermission, requestPermission } = useCameraPermission();
|
||||
|
||||
if (!hasPermission) {
|
||||
requestPermission();
|
||||
}
|
||||
|
||||
// Game type dropdown
|
||||
const [gameTypeOpen, setGameTypeOpen] = useState<boolean>(false);
|
||||
const [gameType, setGameType] = useState<string | null>(null); // This is a dropdown
|
||||
const [gameTypes, setGameTypes] = useState([
|
||||
{ label: "Free Play", value: "freePlay" },
|
||||
{ label: "Straight Pool", value: "straightPool" },
|
||||
{ label: "Nine Ball", value: "nineBall" },
|
||||
]);
|
||||
const onGameTypeOpen = useCallback(() => {
|
||||
setTableSizeOpen(false);
|
||||
setTagsOpen(false);
|
||||
}, []);
|
||||
|
||||
// Table size dropdown
|
||||
const [tableSizeOpen, setTableSizeOpen] = useState<boolean>(false);
|
||||
const [tableSize, setTableSize] = useState<string>("");
|
||||
const [tableSizes, setTableSizes] = useState([
|
||||
{ label: `9'`, value: "nineFoot" },
|
||||
{ label: `8'`, value: "eightFoot" },
|
||||
{ label: "7", value: "sevenFoot" },
|
||||
]);
|
||||
const onTableSizeOpen = useCallback(() => {
|
||||
setGameTypeOpen(false);
|
||||
setTagsOpen(false);
|
||||
}, []);
|
||||
|
||||
// Tags multi-select dropdown
|
||||
const [tagsOpen, setTagsOpen] = useState<boolean>(false);
|
||||
const [tags, setTags] = useState<Array<string>>([]);
|
||||
const [tagsList, setTagsList] = useState([
|
||||
{ label: `Tag1`, value: "tag1" },
|
||||
{ label: `Tag2`, value: "tag2" },
|
||||
{ label: "Tag3", value: "tag3" },
|
||||
]);
|
||||
const onTagsOpen = useCallback(() => {
|
||||
setTableSizeOpen(false);
|
||||
setGameTypeOpen(false);
|
||||
}, []);
|
||||
|
||||
// Location
|
||||
const [location, setLocation] = useState<string>("");
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!hasPermission) {
|
||||
return showAlert("camera");
|
||||
}
|
||||
|
||||
// needs to pass info as params or store in a context/state provider
|
||||
const params: CameraScreenParams = {
|
||||
gameType: gameType,
|
||||
tableSize: tableSize,
|
||||
tags: tags,
|
||||
location: location,
|
||||
};
|
||||
navigation.push("Camera", params);
|
||||
};
|
||||
|
||||
const dropDownStyles = {
|
||||
style: styles.dropdownStyle,
|
||||
};
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
|
||||
<View style={styles.container}>
|
||||
<View style={styles.dropdownContainer}>
|
||||
<Text style={styles.dropdownTitle}>Game Type</Text>
|
||||
<DropDownPicker
|
||||
zIndex={3000}
|
||||
zIndexInverse={1000}
|
||||
open={gameTypeOpen}
|
||||
value={gameType}
|
||||
items={gameTypes}
|
||||
setOpen={setGameTypeOpen}
|
||||
setValue={setGameType}
|
||||
setItems={setGameTypes}
|
||||
onOpen={onGameTypeOpen}
|
||||
{...dropDownStyles}
|
||||
/>
|
||||
<Text style={styles.dropdownTitle}>Table size</Text>
|
||||
<DropDownPicker
|
||||
zIndex={2000}
|
||||
zIndexInverse={2000}
|
||||
open={tableSizeOpen}
|
||||
value={tableSize}
|
||||
items={tableSizes}
|
||||
setOpen={setTableSizeOpen}
|
||||
setValue={setTableSize}
|
||||
setItems={setTableSizes}
|
||||
onOpen={onTableSizeOpen}
|
||||
{...dropDownStyles}
|
||||
/>
|
||||
<Text style={styles.dropdownTitle}>Tags</Text>
|
||||
<DropDownPicker
|
||||
zIndex={1000}
|
||||
zIndexInverse={3000}
|
||||
multiple
|
||||
min={0}
|
||||
max={5}
|
||||
open={tagsOpen}
|
||||
value={tags}
|
||||
items={tagsList}
|
||||
setOpen={setTagsOpen}
|
||||
setValue={setTags}
|
||||
setItems={setTagsList}
|
||||
onOpen={onTagsOpen}
|
||||
{...dropDownStyles}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.dropdownTitle}>Location</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder="Location"
|
||||
value={location}
|
||||
onChangeText={(value) => setLocation(value)}
|
||||
/>
|
||||
|
||||
<View style={styles.buttonContainer}>
|
||||
<TouchableOpacity
|
||||
style={styles.buttonStyle}
|
||||
onPress={() => navigation.goBack()}
|
||||
>
|
||||
<Text style={styles.buttonText}>Back</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.buttonStyle} onPress={handleSubmit}>
|
||||
<Text style={styles.buttonText}>Next</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
@ -7,26 +7,13 @@ export const recordStyles = StyleSheet.create({
|
||||
justifyContent: "center",
|
||||
padding: 20,
|
||||
},
|
||||
dropdownContainer: {
|
||||
width: "100%",
|
||||
marginBottom: 20,
|
||||
zIndex: 50,
|
||||
},
|
||||
dropdownTitle: {
|
||||
fontSize: 16,
|
||||
headerText: {
|
||||
fontSize: 22,
|
||||
fontWeight: "500",
|
||||
marginBottom: 5,
|
||||
alignSelf: "flex-start",
|
||||
},
|
||||
input: {
|
||||
width: "100%",
|
||||
marginBottom: 20,
|
||||
borderWidth: 1,
|
||||
borderColor: "grey",
|
||||
borderRadius: 5,
|
||||
padding: 10,
|
||||
paddingBottom: "10%",
|
||||
},
|
||||
buttonContainer: {
|
||||
marginTop: 10,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
@ -42,16 +29,4 @@ export const recordStyles = StyleSheet.create({
|
||||
color: "white",
|
||||
textAlign: "center",
|
||||
},
|
||||
dropdownStyle: {
|
||||
backgroundColor: "#ffffff",
|
||||
borderColor: "#D1D1D1",
|
||||
borderWidth: 1,
|
||||
borderRadius: 4,
|
||||
},
|
||||
dropdownContainerStyle: {
|
||||
marginBottom: 10,
|
||||
borderColor: "#D1D1D1",
|
||||
borderWidth: 1,
|
||||
borderRadius: 4,
|
||||
},
|
||||
});
|
||||
|
179
src/screens/video-stack/video-details.tsx
Normal file
179
src/screens/video-stack/video-details.tsx
Normal file
@ -0,0 +1,179 @@
|
||||
import * as gql from "railbird-gql";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Keyboard,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
} from "react-native";
|
||||
import DropDownPicker from "react-native-dropdown-picker";
|
||||
// @ts-ignore
|
||||
import { useCameraPermission } from "react-native-vision-camera";
|
||||
import { showAlert } from "../../lib/alert-messages";
|
||||
import { globalInputStyles } from "../../styles";
|
||||
import { recordStyles as styles } from "./styles";
|
||||
import { VideoFlowInputParams } from "./video-stack-types";
|
||||
|
||||
// TerminateUploadStream
|
||||
|
||||
interface VideoDetailsProps {
|
||||
navigation: any;
|
||||
route?: any;
|
||||
}
|
||||
|
||||
export default function VideoDetails({
|
||||
navigation,
|
||||
route,
|
||||
}: VideoDetailsProps): React.JSX.Element {
|
||||
const { hasPermission, requestPermission } = useCameraPermission();
|
||||
const { mode, videoId } = route.params;
|
||||
if (mode === "start-video" && !hasPermission) {
|
||||
requestPermission();
|
||||
}
|
||||
|
||||
// Initial state values based on mode
|
||||
const initialState =
|
||||
mode === "save-video" && route.params ? route.params : {};
|
||||
|
||||
// Session name input
|
||||
// Should session name be required?
|
||||
const [sessionName, setSessionName] = useState<string>(
|
||||
initialState.sessionName || "",
|
||||
);
|
||||
|
||||
// Game type dropdown
|
||||
const [gameTypeOpen, setGameTypeOpen] = useState<boolean>(false);
|
||||
const [gameType, setGameType] = useState<string | null>(
|
||||
initialState.gameType || null,
|
||||
);
|
||||
const [gameTypes, setGameTypes] = useState([
|
||||
{ label: "Free Play", value: "freePlay" },
|
||||
{ label: "Straight Pool", value: "straightPool" },
|
||||
{ label: "Nine Ball", value: "nineBall" },
|
||||
]);
|
||||
const onGameTypeOpen = useCallback(() => {
|
||||
setTableSizeOpen(false);
|
||||
}, []);
|
||||
|
||||
// Table size dropdown
|
||||
const [tableSizeOpen, setTableSizeOpen] = useState<boolean>(false);
|
||||
const [tableSize, setTableSize] = useState<string>(
|
||||
initialState.tableSize || "",
|
||||
);
|
||||
const [tableSizes, setTableSizes] = useState([
|
||||
{ label: `9'`, value: "nineFoot" },
|
||||
{ label: `8'`, value: "eightFoot" },
|
||||
{ label: "7", value: "sevenFoot" },
|
||||
]);
|
||||
const onTableSizeOpen = useCallback(() => {
|
||||
setGameTypeOpen(false);
|
||||
}, []);
|
||||
|
||||
const [TerminateUploadStream, { loading, error }] =
|
||||
gql.useTerminateUploadStreamMutation();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (mode === "start-video" && !hasPermission) {
|
||||
return showAlert("camera");
|
||||
}
|
||||
|
||||
if (mode === "start-video") {
|
||||
const params: VideoFlowInputParams = {
|
||||
sessionName: sessionName,
|
||||
gameType: gameType,
|
||||
tableSize: tableSize,
|
||||
};
|
||||
navigation.push("Camera", params);
|
||||
} else {
|
||||
try {
|
||||
console.log(videoId, sessionName, tableSize, gameType);
|
||||
const res = await TerminateUploadStream({
|
||||
variables: {
|
||||
videoId: videoId,
|
||||
videoName: sessionName,
|
||||
tableSize: tableSize,
|
||||
gameType: gameType,
|
||||
},
|
||||
});
|
||||
console.log(res);
|
||||
navigation.push("Tabs");
|
||||
} catch (err) {
|
||||
console.error(error);
|
||||
return showAlert("terminateUpload");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const dropDownStyles = {
|
||||
style: globalInputStyles.dropdownStyle,
|
||||
};
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.headerText}>
|
||||
{mode === "start-video" ? "Record Session" : "Save Session"}
|
||||
</Text>
|
||||
<View style={globalInputStyles.dropdownContainer}>
|
||||
<Text style={globalInputStyles.dropdownTitle}>Session Name</Text>
|
||||
<TextInput
|
||||
style={globalInputStyles.input}
|
||||
placeholder="Session name"
|
||||
autoCapitalize="none"
|
||||
value={sessionName}
|
||||
onChangeText={setSessionName}
|
||||
/>
|
||||
<Text style={globalInputStyles.dropdownTitle}>Game Type</Text>
|
||||
<DropDownPicker
|
||||
zIndex={3000}
|
||||
zIndexInverse={1000}
|
||||
open={gameTypeOpen}
|
||||
value={gameType}
|
||||
items={gameTypes}
|
||||
setOpen={setGameTypeOpen}
|
||||
setValue={setGameType}
|
||||
setItems={setGameTypes}
|
||||
onOpen={onGameTypeOpen}
|
||||
{...dropDownStyles}
|
||||
/>
|
||||
<Text style={globalInputStyles.dropdownTitle}>Table size</Text>
|
||||
<DropDownPicker
|
||||
zIndex={2000}
|
||||
zIndexInverse={2000}
|
||||
open={tableSizeOpen}
|
||||
value={tableSize}
|
||||
items={tableSizes}
|
||||
setOpen={setTableSizeOpen}
|
||||
setValue={setTableSize}
|
||||
setItems={setTableSizes}
|
||||
onOpen={onTableSizeOpen}
|
||||
{...dropDownStyles}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={styles.buttonContainer}>
|
||||
{mode === "start-video" && (
|
||||
<TouchableOpacity
|
||||
style={styles.buttonStyle}
|
||||
onPress={() => navigation.goBack()}
|
||||
>
|
||||
<Text style={styles.buttonText}>Back</Text>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
<TouchableOpacity style={styles.buttonStyle} onPress={handleSubmit}>
|
||||
{loading ? (
|
||||
<ActivityIndicator color="#fff" />
|
||||
) : (
|
||||
<Text style={styles.buttonText}>
|
||||
{mode === "start-video" ? "Next" : "Save"}
|
||||
</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
6
src/screens/video-stack/video-stack-types.ts
Normal file
6
src/screens/video-stack/video-stack-types.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export interface VideoFlowInputParams {
|
||||
sessionName?: string;
|
||||
gameType: string;
|
||||
tableSize: string;
|
||||
videoId?: number;
|
||||
}
|
@ -1,4 +1,33 @@
|
||||
// GLOBAL STYLES
|
||||
import { StyleSheet } from "react-native";
|
||||
|
||||
export const globalInputStyles = StyleSheet.create({
|
||||
input: {
|
||||
width: "100%",
|
||||
padding: 10,
|
||||
backgroundColor: "#ffffff",
|
||||
borderColor: "#D1D1D1",
|
||||
borderWidth: 1,
|
||||
borderRadius: 4,
|
||||
},
|
||||
dropdownContainer: {
|
||||
width: "100%",
|
||||
marginBottom: 20,
|
||||
zIndex: 50,
|
||||
},
|
||||
dropdownTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: "500",
|
||||
marginBottom: 5,
|
||||
alignSelf: "flex-start",
|
||||
},
|
||||
dropdownStyle: {
|
||||
backgroundColor: "#ffffff",
|
||||
borderColor: "#D1D1D1",
|
||||
borderWidth: 1,
|
||||
borderRadius: 4,
|
||||
},
|
||||
});
|
||||
// COLORS:
|
||||
// can be made more granular to specify utility (ex: fontColors vs backgroundColors)
|
||||
export const colors = {
|
||||
|
@ -9798,9 +9798,9 @@ queue@6.0.2:
|
||||
dependencies:
|
||||
inherits "~2.0.3"
|
||||
|
||||
"railbird-gql@git+https://dev.railbird.ai/railbird/railbird-gql.git#master":
|
||||
"railbird-gql@git+https://dev.railbird.ai/railbird/railbird-gql.git#234d4d0fa90342f8af655c9ddf476f033caa322d":
|
||||
version "1.0.0"
|
||||
resolved "git+https://dev.railbird.ai/railbird/railbird-gql.git#204e289627b08a96b947b6f23ed4a843d9e8abff"
|
||||
resolved "git+https://dev.railbird.ai/railbird/railbird-gql.git#234d4d0fa90342f8af655c9ddf476f033caa322d"
|
||||
dependencies:
|
||||
"@apollo/client" "^3.9.2"
|
||||
"@graphql-codegen/cli" "^5.0.0"
|
||||
|
Loading…
Reference in New Issue
Block a user