Compare commits

...

10 Commits

Author SHA1 Message Date
Dean Wenstrand
d4f4a539f5 Add camera-claim schema: venues, cameras, claim lifecycle
New query/mutation surface for the camera-claim flow: getVenues (with
per-camera availability), getActiveClaim, claimCamera, endClaim,
extendClaim, plus VenueGQL / VenueCameraGQL / CameraClaimGQL /
ClaimStatusEnum types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 14:44:33 -07:00
7cef75b2ca Merge pull request 'dean/get-video-card-query' (#250) from dean/get-video-card-query into master
Reviewed-on: #250
2026-06-09 05:05:52 +00:00
9358205327 Merge pull request 'Add quotaEnforcementEnabled to deployed config' (#249) from loewy/expose-quota-enforcement-config into master
Reviewed-on: #249
2026-05-28 23:18:01 +00:00
88634a32e9 add quotaEnforcementEnabled to deployed config
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2026-05-28 14:52:51 -07:00
f7200a2e9f Merge pull request 'Add upload quota status operation' (#248) from loewy/quota-status-buckets into master
Reviewed-on: #248
2026-05-28 21:46:56 +00:00
7d839c0fa6 Add upload quota status operation
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2026-05-27 16:52:43 -07:00
ab1e604871 Merge pull request 'Add quota bucket status fields' (#247) from loewy/quota-status-buckets into master
Reviewed-on: #247
2026-05-26 02:47:40 +00:00
deb724b430 Add quota bucket status fields
All checks were successful
Tests / Tests (pull_request) Successful in 9s
2026-05-25 16:26:57 -07:00
c586bdf1a6 Merge pull request 'Add username + profileImageUri to PlayerClusterGQL' (#246) from dean/player-cluster-username into master
Reviewed-on: #246
2026-05-12 00:27:06 +00:00
a12b3e1210 Merge pull request 'Add averageDifficulty to PlayerSummaryFields' (#245) from dean/player-summaries-avg-difficulty-types into master
Reviewed-on: #245
2026-05-11 22:41:52 +00:00
4 changed files with 316 additions and 0 deletions

View File

@@ -140,6 +140,18 @@ export type BucketSetInputGql = {
feature: Scalars["String"]["input"];
};
export type CameraClaimGql = {
__typename?: "CameraClaimGQL";
cameraId: Scalars["Int"]["output"];
createdAt: Scalars["DateTime"]["output"];
endedAt?: Maybe<Scalars["DateTime"]["output"]>;
expiresAt?: Maybe<Scalars["DateTime"]["output"]>;
id: Scalars["Int"]["output"];
status: ClaimStatusEnum;
userId: Scalars["Int"]["output"];
videoId?: Maybe<Scalars["Int"]["output"]>;
};
export type CancellationFeedbackMetadataInput = {
appVersion?: InputMaybe<Scalars["String"]["input"]>;
gitRevision?: InputMaybe<Scalars["String"]["input"]>;
@@ -200,6 +212,21 @@ export type ChallengeInvitation = {
status: Scalars["String"]["output"];
};
export enum ClaimStatusEnum {
Active = "ACTIVE",
CodeExpired = "CODE_EXPIRED",
CodeIssued = "CODE_ISSUED",
EndedByUser = "ENDED_BY_USER",
Expired = "EXPIRED",
Ingesting = "INGESTING",
ReleasedInactive = "RELEASED_INACTIVE",
Requested = "REQUESTED",
Scanning = "SCANNING",
ScanConfirmed = "SCAN_CONFIRMED",
StreamLost = "STREAM_LOST",
StreamUnreachable = "STREAM_UNREACHABLE",
}
export enum ClientUploadStatusEnum {
UploadDisabled = "UPLOAD_DISABLED",
UploadEnabled = "UPLOAD_ENABLED",
@@ -290,6 +317,7 @@ export type DeployedConfigGql = {
environment: Scalars["String"]["output"];
firebase: Scalars["Boolean"]["output"];
minimumAllowedAppVersion: Scalars["String"]["output"];
quotaEnforcementEnabled: Scalars["Boolean"]["output"];
subscriptionGatingEnabled: Scalars["Boolean"]["output"];
};
@@ -2387,6 +2415,7 @@ export type Mutation = {
blockContent: Scalars["Boolean"]["output"];
blockUser: Scalars["Boolean"]["output"];
cancelSubscription: UserSubscriptionStatusGql;
claimCamera: CameraClaimGql;
commentOnVideo: Scalars["Boolean"]["output"];
createBucketSet: BucketSetGql;
createChallenge: Challenge;
@@ -2405,7 +2434,9 @@ export type Mutation = {
editShot: EditShotReturn;
editUploadStream: Scalars["Boolean"]["output"];
editUser: UserGql;
endClaim: CameraClaimGql;
ensureStripeCustomerExists: UserGql;
extendClaim: CameraClaimGql;
finalizePlayerAssignments: Array<PlayerClusterGql>;
findPrerecordTableLayout?: Maybe<HomographyInfoGql>;
followUser: UserGql;
@@ -2447,6 +2478,11 @@ export type MutationBlockUserArgs = {
userId: Scalars["Int"]["input"];
};
export type MutationClaimCameraArgs = {
cameraId: Scalars["Int"]["input"];
durationMinutes?: Scalars["Int"]["input"];
};
export type MutationCommentOnVideoArgs = {
message: Scalars["String"]["input"];
parentCommentId?: InputMaybe<Scalars["Int"]["input"]>;
@@ -2530,6 +2566,15 @@ export type MutationEditUserArgs = {
input: EditUserInputGql;
};
export type MutationEndClaimArgs = {
claimId: Scalars["Int"]["input"];
};
export type MutationExtendClaimArgs = {
additionalMinutes?: Scalars["Int"]["input"];
claimId: Scalars["Int"]["input"];
};
export type MutationFinalizePlayerAssignmentsArgs = {
input: FinalizePlayerAssignmentsInput;
};
@@ -2812,6 +2857,7 @@ export type Query = {
challengeLeaderboard: Array<ChallengeEntry>;
challenges: Array<Challenge>;
doesUsernameExist: Scalars["Boolean"]["output"];
getActiveClaim?: Maybe<CameraClaimGql>;
getAggregatedShotMetrics: Array<AggregateResultGql>;
getAvailableSubscriptionOptions: StripeSubscriptionOptionsGql;
getBucketSet?: Maybe<BucketSetGql>;
@@ -2838,6 +2884,7 @@ export type Query = {
getUserVideos: VideoHistoryGql;
getUsernames: Array<Scalars["String"]["output"]>;
getUsersMatching: Array<UserGql>;
getVenues: Array<VenueGql>;
getVideo: VideoGql;
getVideoMakePercentageIntervals: Array<MakePercentageIntervalGql>;
getVideos: Array<VideoGql>;
@@ -3023,15 +3070,29 @@ export type QueryWaitForArgs = {
duration: Scalars["Float"]["input"];
};
export type QuotaBucketStatusGql = {
__typename?: "QuotaBucketStatusGQL";
appliesToUploadKind: Scalars["String"]["output"];
canUpload: Scalars["Boolean"]["output"];
durationLimitSeconds?: Maybe<Scalars["Int"]["output"]>;
durationRemainingSeconds?: Maybe<Scalars["Float"]["output"]>;
durationUsedSeconds: Scalars["Float"]["output"];
periodEnd: Scalars["DateTime"]["output"];
periodStart: Scalars["DateTime"]["output"];
quotaKey: Scalars["String"]["output"];
};
export type QuotaStatusGql = {
__typename?: "QuotaStatusGQL";
canUpload: Scalars["Boolean"]["output"];
durationLimitSeconds?: Maybe<Scalars["Int"]["output"]>;
durationRemainingSeconds?: Maybe<Scalars["Float"]["output"]>;
durationUsedSeconds: Scalars["Float"]["output"];
importQuotaBuckets: Array<QuotaBucketStatusGql>;
maxVideoDurationSeconds?: Maybe<Scalars["Int"]["output"]>;
periodEnd: Scalars["DateTime"]["output"];
periodStart: Scalars["DateTime"]["output"];
recordingQuotaBuckets: Array<QuotaBucketStatusGql>;
tierName: Scalars["String"]["output"];
};
@@ -3527,6 +3588,24 @@ export type UserSubscriptionStatusGql = {
validUntil?: Maybe<Scalars["DateTime"]["output"]>;
};
export type VenueCameraGql = {
__typename?: "VenueCameraGQL";
enabled: Scalars["Boolean"]["output"];
id: Scalars["Int"]["output"];
isAvailable: Scalars["Boolean"]["output"];
tableLabel: Scalars["String"]["output"];
venueId: Scalars["Int"]["output"];
};
export type VenueGql = {
__typename?: "VenueGQL";
cameras: Array<VenueCameraGql>;
id: Scalars["Int"]["output"];
latitude?: Maybe<Scalars["Float"]["output"]>;
longitude?: Maybe<Scalars["Float"]["output"]>;
name: Scalars["String"]["output"];
};
export type VideoFeedInputGql =
| {
allUsers: Scalars["Boolean"]["input"];
@@ -4119,6 +4198,7 @@ export type GetDeployedConfigQuery = {
firebase: boolean;
minimumAllowedAppVersion: string;
subscriptionGatingEnabled: boolean;
quotaEnforcementEnabled: boolean;
defaultAndroidRecordingFormat: StreamSegmentTypeEnum;
bucketUrl: string;
bannerMessages: Array<{
@@ -5784,6 +5864,47 @@ export type GetUserPlayTimeQuery = {
getPlayTime: { __typename?: "UserPlayTimeGQL"; totalSeconds: number };
};
export type GetUploadQuotaStatusQueryVariables = Exact<{
[key: string]: never;
}>;
export type GetUploadQuotaStatusQuery = {
__typename?: "Query";
getQuotaStatus: {
__typename?: "QuotaStatusGQL";
tierName: string;
periodStart: any;
periodEnd: any;
durationUsedSeconds: number;
durationLimitSeconds?: number | null;
maxVideoDurationSeconds?: number | null;
durationRemainingSeconds?: number | null;
canUpload: boolean;
importQuotaBuckets: Array<{
__typename?: "QuotaBucketStatusGQL";
quotaKey: string;
appliesToUploadKind: string;
periodStart: any;
periodEnd: any;
durationUsedSeconds: number;
durationLimitSeconds?: number | null;
durationRemainingSeconds?: number | null;
canUpload: boolean;
}>;
recordingQuotaBuckets: Array<{
__typename?: "QuotaBucketStatusGQL";
quotaKey: string;
appliesToUploadKind: string;
periodStart: any;
periodEnd: any;
durationUsedSeconds: number;
durationLimitSeconds?: number | null;
durationRemainingSeconds?: number | null;
canUpload: boolean;
}>;
};
};
export type GetUsernamesQueryVariables = Exact<{
matchString: Scalars["String"]["input"];
limit?: InputMaybe<Scalars["Int"]["input"]>;
@@ -8734,6 +8855,7 @@ export const GetDeployedConfigDocument = gql`
firebase
minimumAllowedAppVersion
subscriptionGatingEnabled
quotaEnforcementEnabled
bannerMessages {
color
dismissible
@@ -12099,6 +12221,105 @@ export type GetUserPlayTimeQueryResult = Apollo.QueryResult<
GetUserPlayTimeQuery,
GetUserPlayTimeQueryVariables
>;
export const GetUploadQuotaStatusDocument = gql`
query GetUploadQuotaStatus {
getQuotaStatus {
tierName
periodStart
periodEnd
durationUsedSeconds
durationLimitSeconds
maxVideoDurationSeconds
durationRemainingSeconds
canUpload
importQuotaBuckets {
quotaKey
appliesToUploadKind
periodStart
periodEnd
durationUsedSeconds
durationLimitSeconds
durationRemainingSeconds
canUpload
}
recordingQuotaBuckets {
quotaKey
appliesToUploadKind
periodStart
periodEnd
durationUsedSeconds
durationLimitSeconds
durationRemainingSeconds
canUpload
}
}
}
`;
/**
* __useGetUploadQuotaStatusQuery__
*
* To run a query within a React component, call `useGetUploadQuotaStatusQuery` and pass it any options that fit your needs.
* When your component renders, `useGetUploadQuotaStatusQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetUploadQuotaStatusQuery({
* variables: {
* },
* });
*/
export function useGetUploadQuotaStatusQuery(
baseOptions?: Apollo.QueryHookOptions<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>(GetUploadQuotaStatusDocument, options);
}
export function useGetUploadQuotaStatusLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>(GetUploadQuotaStatusDocument, options);
}
export function useGetUploadQuotaStatusSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>(GetUploadQuotaStatusDocument, options);
}
export type GetUploadQuotaStatusQueryHookResult = ReturnType<
typeof useGetUploadQuotaStatusQuery
>;
export type GetUploadQuotaStatusLazyQueryHookResult = ReturnType<
typeof useGetUploadQuotaStatusLazyQuery
>;
export type GetUploadQuotaStatusSuspenseQueryHookResult = ReturnType<
typeof useGetUploadQuotaStatusSuspenseQuery
>;
export type GetUploadQuotaStatusQueryResult = Apollo.QueryResult<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>;
export const GetUsernamesDocument = gql`
query getUsernames(
$matchString: String!

View File

@@ -6,6 +6,7 @@ query getDeployedConfig {
firebase
minimumAllowedAppVersion
subscriptionGatingEnabled
quotaEnforcementEnabled
bannerMessages {
color
dismissible

View File

@@ -49,6 +49,39 @@ query GetUserPlayTime($userId: Int!, $filters: VideoFilterInput) {
}
}
query GetUploadQuotaStatus {
getQuotaStatus {
tierName
periodStart
periodEnd
durationUsedSeconds
durationLimitSeconds
maxVideoDurationSeconds
durationRemainingSeconds
canUpload
importQuotaBuckets {
quotaKey
appliesToUploadKind
periodStart
periodEnd
durationUsedSeconds
durationLimitSeconds
durationRemainingSeconds
canUpload
}
recordingQuotaBuckets {
quotaKey
appliesToUploadKind
periodStart
periodEnd
durationUsedSeconds
durationLimitSeconds
durationRemainingSeconds
canUpload
}
}
}
query getUsernames(
$matchString: String!
$limit: Int = null

View File

@@ -11,6 +11,8 @@ type Query {
myChallengeInvitations: [ChallengeInvitation!]!
ruleSets: [RuleSet!]!
myChallengeEntries: [ChallengeEntry!]!
getVenues: [VenueGQL!]!
getActiveClaim: CameraClaimGQL
getDeployedConfig: DeployedConfigGQL!
waitFor(duration: Float!): Float!
getFeedVideos(
@@ -681,6 +683,48 @@ type PlayerSummaryGQL {
averageTimeBetweenShots: Float
}
type VenueGQL {
id: Int!
name: String!
latitude: Float
longitude: Float
cameras: [VenueCameraGQL!]!
}
type VenueCameraGQL {
id: Int!
venueId: Int!
tableLabel: String!
enabled: Boolean!
isAvailable: Boolean!
}
type CameraClaimGQL {
id: Int!
cameraId: Int!
userId: Int!
videoId: Int
status: ClaimStatusEnum!
expiresAt: DateTime
endedAt: DateTime
createdAt: DateTime!
}
enum ClaimStatusEnum {
REQUESTED
CODE_ISSUED
SCANNING
SCAN_CONFIRMED
INGESTING
ACTIVE
ENDED_BY_USER
EXPIRED
STREAM_LOST
STREAM_UNREACHABLE
CODE_EXPIRED
RELEASED_INACTIVE
}
type DeployedConfigGQL {
allowNewUsers: Boolean!
firebase: Boolean!
@@ -688,6 +732,7 @@ type DeployedConfigGQL {
environment: String!
minimumAllowedAppVersion: String!
subscriptionGatingEnabled: Boolean!
quotaEnforcementEnabled: Boolean!
bannerMessages: [BannerGQL!]!
defaultAndroidRecordingFormat: StreamSegmentTypeEnum!
bucketUrl: String!
@@ -1040,6 +1085,19 @@ type QuotaStatusGQL {
durationUsedSeconds: Float!
durationLimitSeconds: Int
maxVideoDurationSeconds: Int
importQuotaBuckets: [QuotaBucketStatusGQL!]!
recordingQuotaBuckets: [QuotaBucketStatusGQL!]!
durationRemainingSeconds: Float
canUpload: Boolean!
}
type QuotaBucketStatusGQL {
quotaKey: String!
appliesToUploadKind: String!
periodStart: DateTime!
periodEnd: DateTime!
durationUsedSeconds: Float!
durationLimitSeconds: Int
durationRemainingSeconds: Float
canUpload: Boolean!
}
@@ -1115,6 +1173,9 @@ type Mutation {
submitChallengeEntry(entryId: ID!, videoId: ID!): ChallengeEntry!
dismissChallenge(challengeId: ID!): Boolean!
undismissChallenge(challengeId: ID!): Boolean!
claimCamera(cameraId: Int!, durationMinutes: Int! = 60): CameraClaimGQL!
endClaim(claimId: Int!): CameraClaimGQL!
extendClaim(claimId: Int!, additionalMinutes: Int! = 60): CameraClaimGQL!
setLoggerLevel(path: String!, level: String!): Boolean!
reactToVideo(videoId: Int!, reaction: ReactionEnum): Boolean!
commentOnVideo(