adding in qt cli project instead of using raw c++ and/or golang FFI

This commit is contained in:
talksik
2025-02-22 08:45:54 -08:00
parent 7fcc9b8177
commit 05a6d677a4
32 changed files with 213 additions and 1 deletions
BIN
View File
Binary file not shown.
+23
View File
@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.10)
project(FlowyAdmin)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Add the external library directory
include_directories(external)
include_directories(go)
add_executable(flowy_admin src/main.cpp)
# Link the Go shared library (using an absolute path)
target_link_libraries(flowy_admin "${CMAKE_SOURCE_DIR}/go/libgraphql.dylib")
# Set the RPATH so the executable knows where to find the shared library at runtime.
# Here we embed the absolute path to the 'go' directory.
set_target_properties(flowy_admin PROPERTIES
BUILD_RPATH "${CMAKE_SOURCE_DIR}/go"
INSTALL_RPATH "${CMAKE_SOURCE_DIR}/go"
)
+1
View File
@@ -0,0 +1 @@
# admin-cli
+8
View File
@@ -0,0 +1,8 @@
[
{
"directory": "/Users/talksik/Documents/code/admin-cli/build",
"command": "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -I/Users/talksik/Documents/code/admin-cli/external -I/Users/talksik/Documents/code/admin-cli/go -std=gnu++17 -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk -o CMakeFiles/flowy_admin.dir/src/main.cpp.o -c /Users/talksik/Documents/code/admin-cli/src/main.cpp",
"file": "/Users/talksik/Documents/code/admin-cli/src/main.cpp",
"output": "CMakeFiles/flowy_admin.dir/src/main.cpp.o"
}
]
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
set -e
rm -rf ./src/generated
mkdir -p ./src/generated
./external/cppgraphqlgen/build/src/clientgen -s ./src/schema.graphqls \
-r ./src/query.graphql \
--source-dir ./src/generated \
--header-dir ./src/generated \
-p Client \
-n graphql
BIN
View File
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
set -e
go build -o libgraphql.dylib -buildmode=c-shared cmd/main.go
+48
View File
@@ -0,0 +1,48 @@
package main
import (
"C"
"fmt"
"github.com/Khan/genqlient/graphql"
)
import (
"context"
"net/http"
"unsafe"
"github.com/flowy-live/admin-cli/go/internal/graph"
"github.com/sirupsen/logrus"
)
//export QueryGraphQL
func QueryGraphQL(query *C.char) *C.char {
goQuery := C.GoString(query)
result := fmt.Sprintf("GraphQL Response for: %s", goQuery)
return C.CString(result)
}
//export GetWaitlist
func GetWaitlist() (**C.char, C.long) {
heliosAddr := "https://helios.dev.flowy.live"
heliosFullAddr := fmt.Sprintf("%s/%s", heliosAddr, "query")
gqlClient := graphql.NewClient(heliosFullAddr, http.DefaultClient)
response, err := graph.GetWaitlist(context.Background(), gqlClient)
if err != nil {
logrus.Error(err)
}
cArray := C.malloc(C.size_t(len(response.GetWaitlist)+1) * C.size_t(unsafe.Sizeof(uintptr(0))))
arrayPtr := (*[1 << 30]*C.char)(cArray)[: len(response.GetWaitlist)+1 : len(response.GetWaitlist)+1] // Unsafe slice
for i, s := range response.GetWaitlist {
arrayPtr[i] = C.CString(s.Email)
}
arrayPtr[len(response.GetWaitlist)] = nil // Null-terminate
return (**C.char)(cArray), 0
}
func main() {
}
+3
View File
@@ -0,0 +1,3 @@
package tools
//go:generate go run github.com/Khan/genqlient@latest ./internal/graph/genqlient.yaml
+14
View File
@@ -0,0 +1,14 @@
module github.com/flowy-live/admin-cli/go
go 1.24.0
require (
github.com/Khan/genqlient v0.8.0
github.com/sirupsen/logrus v1.9.3
)
require (
github.com/google/uuid v1.6.0 // indirect
github.com/vektah/gqlparser/v2 v2.5.19 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
)
+27
View File
@@ -0,0 +1,27 @@
github.com/Khan/genqlient v0.8.0 h1:Hd1a+E1CQHYbMEKakIkvBH3zW0PWEeiX6Hp1i2kP2WE=
github.com/Khan/genqlient v0.8.0/go.mod h1:hn70SpYjWteRGvxTwo0kfaqg4wxvndECGkfa1fdDdYI=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/vektah/gqlparser/v2 v2.5.19 h1:bhCPCX1D4WWzCDvkPl4+TP1N8/kLrWnp43egplt7iSg=
github.com/vektah/gqlparser/v2 v2.5.19/go.mod h1:y7kvl5bBlDeuWIvLtA9849ncyvx6/lj06RsMrEjVy3U=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -0,0 +1,58 @@
// Code generated by github.com/Khan/genqlient, DO NOT EDIT.
package graph
import (
"context"
"github.com/Khan/genqlient/graphql"
)
// GetWaitlistGetWaitlistWaitlistEntry includes the requested fields of the GraphQL type WaitlistEntry.
type GetWaitlistGetWaitlistWaitlistEntry struct {
Email string `json:"email"`
}
// GetEmail returns GetWaitlistGetWaitlistWaitlistEntry.Email, and is useful for accessing the field via an interface.
func (v *GetWaitlistGetWaitlistWaitlistEntry) GetEmail() string { return v.Email }
// GetWaitlistResponse is returned by GetWaitlist on success.
type GetWaitlistResponse struct {
// Roles: flowy-admin
GetWaitlist []GetWaitlistGetWaitlistWaitlistEntry `json:"getWaitlist"`
}
// GetGetWaitlist returns GetWaitlistResponse.GetWaitlist, and is useful for accessing the field via an interface.
func (v *GetWaitlistResponse) GetGetWaitlist() []GetWaitlistGetWaitlistWaitlistEntry {
return v.GetWaitlist
}
// The query executed by GetWaitlist.
const GetWaitlist_Operation = `
query GetWaitlist {
getWaitlist {
email
}
}
`
func GetWaitlist(
ctx_ context.Context,
client_ graphql.Client,
) (data_ *GetWaitlistResponse, err_ error) {
req_ := &graphql.Request{
OpName: "GetWaitlist",
Query: GetWaitlist_Operation,
}
data_ = &GetWaitlistResponse{}
resp_ := &graphql.Response{Data: data_}
err_ = client_.MakeRequest(
ctx_,
req_,
resp_,
)
return data_, err_
}
@@ -0,0 +1,5 @@
query GetWaitlist {
getWaitlist {
email
}
}
@@ -0,0 +1,14 @@
# Default genqlient config; for full documentation see:
# https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
schema: schema.graphql
operations:
- genqlient.graphql
generated: generated.go
package: graph
optional: pointer
bindings:
# To bind a scalar:
DateTime:
type: time.Time
URL:
type: string
@@ -0,0 +1,861 @@
scalar URL
scalar Time
scalar Upload
enum ErrorCode {
NOT_REGISTERED
ALREADY_REGISTERED
INVALID_EMAIL
INVALID_CODE
BAD_USER_INPUT
UNAUTHENTICATED
FORBIDDEN
NOT_FOUND
INTERNAL_SERVER_ERROR
SIGNIN_CODE_EXPIRED_OR_NOT_FOUND
}
type Query {
"""
Roles: flowy-admin
"""
getWaitlist: [WaitlistEntry!]!
"""
Temporary public way to pull all humans.
"""
humans: [Human!]!
"""
"""
human(id: ID!): Human!
"""
[Temporary] Must set 'Authorization' header to auth-token.
"""
viewer: Human!
"""
Search for humans including contacts by their display name, email.
"""
searchHumans(query: String!): [Human!]!
"""
Returns all of the spaces for the authed human.
"""
spaces: SpaceConnection!
}
type Subscription {
"""
Subscribes to new echoes relevant to the authed human. This means the echo is in 'SENT' state.
Includes echoes sent by the authed human.
"""
echoSent: Echo!
"""
Notifies authed human when relevant echo is expired.
"""
echoExpired(echoId: ID!): Echo!
"""
New conversation created which involves the authed human.
"""
newConversation: ID!
"""
Watch the basic online status of a human.
"""
humanStatus(humanId: ID!): Boolean!
"""
Watches for signals for people echoing in a conversation.
"""
echoSignal(conversationId: ID!): ID!
"""
Receive instructions (for the chrome extension) sent by given human on the keypad.
"""
instruction(humanId: ID!): Instruction!
"""
Receive messages from the chrome extension.
"""
peripheralMessage: PeripheralMessage!
}
type OpenUrlInstruction {
url: URL!
}
type ChatGptFollowUpInstruction {
query: String!
}
type ChatGptToggleWebSearchInstruction {
# FIX: required to have one field for some reason
fakeField: Boolean
}
type GoogleCalendarUpdateViewInstruction {
hotkey: String!
}
type VariableInstruction {
"""
Will be a json representation of a instruction defined in keypad & chrome extension.
"""
message: String!
}
union Instruction = OpenUrlInstruction | ChatGptFollowUpInstruction | ChatGptToggleWebSearchInstruction | GoogleCalendarUpdateViewInstruction | VariableInstruction
type UrlUpdatedMessage {
currentUrl: URL!
}
type StateUpdateMessage {
url: URL!
"""
Json representation for specific application context.
"""
state: String
}
union PeripheralMessage = UrlUpdatedMessage | StateUpdateMessage
type Mutation {
"""
Registers a new human.
Errors if already registered.
"""
register(email: String!, displayName: String!): Boolean!
"""
Sends email with a code.
ErrorCodeNotFound if not registered.
"""
signIn(email: String!): Boolean!
"""
Verifies the code and returns a token.
ErrorCodeNotFound if not registered.
ErrorSignInCodeExpiredOrNotFound if the code is expired or not found.
"""
signInVerify(email: String!, code: String!): TokenOutput!
"""
If already in the waitlist, gracefully returns success.
Roles: public
"""
joinWaitlist(input: JoinWaitlistInput!): Boolean!
"""
Add a human into the system.
Returns the human.
"""
addHuman(input: AddHumanInput!): Human!
"""
The first step in flowy.llink (thought).
Returns ID of the draft echo.
"""
draftEcho(input: DraftEchoInput!): DraftEchoOutput!
"""
The second step in flowy.llink (top-of-mind people).
Requires authentication as the human who drafted.
The system will prepare and send the echo after this step and you will be notified in echoReceived subscription.
Returns true if successful.
"""
commitEcho(input: CommitEchoInput!): Boolean!
"""
Adds a human to a conversation.
Requires authentication as the admin of the conversation.
"""
addHumanToConversation(input: AddHumanToConversationInput!): Boolean!
"""
Updates the profile picture of the authed human.
"""
updateHumanProfile(input: UpdateHumanProfileInput!): Boolean!
"""
Updates the playback marker for the authed human in a conversation.
Must be a member of the conversation.
"""
updatePlaybackMarker(conversationId: ID!, echoId: ID!, currentPositionSeconds: Int!): ConversationPlaybackMarker!
"""
Updates the last seen time of the authed human.
"""
updateLastSeen: Boolean!
"""
Updates the presence of the authed human.
"""
disconnectPresence: Boolean!
"""
Must be a member of the conversation.
Signals to others in the conversation that the authed human is echoing.
"""
sendEchoSignal(conversationId: ID!): Boolean!
"""
Generates a token to join the live room.
"""
generateLiveToken(conversationId: ID!): String!
"""
Sends instruction to chrome extension.
"""
sendInstruction(input: SendInstructionInput!): Boolean!
"""
For sending responses/messages from the chrome extension to the keypad.
"""
sendMessageToKeypad(input: SendMessageToKeypadInput!): Boolean!
addSpace(input: AddSpaceInput!): Space!
updateSpace(input: UpdateSpaceInput!): Space!
moveSpace(input: MoveSpaceInput!): Boolean!
deleteSpace(input: DeleteSpaceInput!): Boolean!
setKey(input: SetKeyInput!): Key!
swapKeys(input: SwapKeysInput!): Boolean!
deleteKey(input: DeleteKeyInput!): Boolean!
}
input AddSpaceInput {
name: String!
"""
Optional. Either set this or `iconUrl`, not both.
"""
iconName: String
"""
Optional. Either set this or `iconName`, not both.
"""
iconUrl: URL
"""
Optional. Either specify a domain or an app name followed by the extension
(e.g. 'app:Visual Studio Code' or 'url_contains:flowy.com' or 'url_contains:youtube.com/watch').
"""
trigger: String
}
input UpdateSpaceInput {
spaceId: ID!
name: String!
"""
Optional. Either set this or `iconUrl`, not both.
"""
iconName: String
"""
Optional. Either set this or `iconName`, not both.
"""
iconUrl: URL
"""
Optional. Either specify a domain or an app name followed by the extension
(e.g. 'app:Visual Studio Code' or 'url_contains:flowy.com' or 'url_contains:youtube.com/watch').
"""
trigger: String
}
enum MoveSpaceDirection {
UP
DOWN
}
input MoveSpaceInput {
spaceId: ID!
direction: MoveSpaceDirection!
}
input DeleteSpaceInput {
spaceId: ID!
}
input KeyTypeKeypressInput {
mainKey: String!
shiftKey: Boolean!
commandKey: Boolean!
optionKey: Boolean!
controlKey: Boolean!
}
input KeyTypeOpenUrlInput {
url: String! # URL is typically represented as a String in GraphQL
newWindow: Boolean!
}
input KeyTypeOpenAppInput {
appName: String!
}
input KeyTypeTextSnippetInput {
text: String!
pasteInCurrentInput: Boolean!
copyToClipboard: Boolean!
}
input KeyTypeTerminalCommandInput {
command: String!
directory: String!
}
input KeyTypeWriteForMeInput {
prependedInstruction: String!
writingStyle: String
formatForCurrentApp: Boolean!
attachScreenshot: Boolean!
}
input KeyTypeTypeForMeInput {
minimallyProcess: Boolean!
formatForCurrentApp: Boolean!
}
input KeyTypeFlowyChatInput {
prependedInstruction: String!
}
input KeyTypeMacOsCommandInput {
commandType: MacOsCommandType!
}
input KeyConfigurationInput {
keypress: KeyTypeKeypressInput
openUrl: KeyTypeOpenUrlInput
openApp: KeyTypeOpenAppInput
textSnippet: KeyTypeTextSnippetInput
terminalCommand: KeyTypeTerminalCommandInput
writeForMe: KeyTypeWriteForMeInput
typeForMe: KeyTypeTypeForMeInput
flowyChat: KeyTypeFlowyChatInput
macOsCommand: KeyTypeMacOsCommandInput
}
input SetKeyInput {
spaceId: ID!
position: Int!
title: String!
"""
Optional. Either set this or `iconUrl`, not both.
"""
iconName: String
"""
Optional. Either set this or `iconName`, not both.
"""
iconUrl: URL
hexColor: String
"""
Must specify one of the inputs.
"""
keyConfiguration: KeyConfigurationInput!
}
input SwapKeysInput {
spaceId: ID!
positionA: Int!
positionB: Int!
}
input DeleteKeyInput {
spaceId: ID!
position: Int!
}
enum Role {
ADMIN,
HUMAN,
}
type TokenOutput {
token: String!
role: Role!
}
input UrlUpdatedMessageInput {
"""
Current url of the active tab and focused window.
"""
currentUrl: URL!
}
input StateUpdateInput {
url: URL!
"""
State of specific site in url in json form.
"""
state: String
}
input SendMessageToKeypadInput {
humanId: ID!
urlUpdated: UrlUpdatedMessageInput
stateUpdate: StateUpdateInput
}
input SendInstructionUpdateGoogleCalendarViewInput {
"""
The hotkey to update the google calendar view.
"""
hotkey: String!
}
input SendInstructionOpenUrlInput {
url: URL!
}
input SendInstructionChatGptFollowUpInput {
query: String!
}
input SendInstructionChatGptToggleWebSearchInput {
# FIX: required to have one field for some reason
fakeField: Boolean
}
input SendVariableInstructionInput {
"""
Will be a json representation of a instruction defined in keypad & chrome extension.
"""
message: String!
}
### FIX: dealing with polymorphism here by having different optional fields because can't have a union in an input type
input SendInstructionInput {
openUrl: SendInstructionOpenUrlInput
chatGptFollowUp: SendInstructionChatGptFollowUpInput
chatGptToggleWebSearch: SendInstructionChatGptToggleWebSearchInput
updateGoogleCalendarView: SendInstructionUpdateGoogleCalendarViewInput
variable: SendVariableInstructionInput
}
type Header {
key: String!
value: String!
}
type DraftEchoOutput {
draftEchoId: ID!
"""
The URL to upload the asset to. Must be done before committing the echo.
"""
assetUploadUrl: URL!
"""
Use these headers to upload the asset to the given URL.
"""
uploadHeaders: [Header!]!
}
enum CompanyWorkspaceSuite {
GSuite
Office365
}
type WaitlistEntry {
email: String!
firstName: String
lastName: String
companyWebsite: URL
companyWorkspaceSuite: CompanyWorkspaceSuite
isDecisionMaker: Boolean
}
input JoinWaitlistInput {
email: String!
firstName: String
lastName: String
companyWebsite: URL
companyWorkspaceSuite: CompanyWorkspaceSuite
isDecisionMaker: Boolean
}
input AddHumanInput {
email: String!
displayName: String!
profilePicture: Upload
}
input DraftEchoInput {
contentLength: Int!
contentType: String!
"""
Must match the mime type of the asset. If `video/*`, then you can use `VIDEO` or `SCREEN`.
"""
echoType: EchoType!
}
input CommitEchoInput {
draftEchoId: ID!
"""
Send to the given existing conversation.
Authed human must be a member of the conversation.
Must have this or `newConversation`.
"""
existingConversation: String
"""
Send to a new conversation with the given human ids.
Authed human becomes the admin of the conversation.
Must have this or `existingConversation`.
Do NOT include the echo creator in the list.
"""
newConversation: [ID!]
}
input AddHumanToConversationInput {
conversationId: ID!
humanId: ID!
}
input UpdateHumanProfileInput {
"""
The profile picture to update. Optional.
If kept empty, will retain the former profile picture.
"""
profilePicture: Upload
"""
The display name to update. Optional.
If kept empty, will retain the former display name.
"""
displayName: String
}
"""
Orders by all types DESC
"""
enum PaginationOrderBy {
CREATED_AT
UPDATED_AT
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
type Human {
id: ID!
email: String!
displayName: String!
profilePictureUrl: URL
"""
Two letters based on the display name.
"""
initials: String!
isFlowyAdmin: Boolean!
createdAt: Time!
"""
Conversations that the human is involved.
Only available if the viewer is this human or flowy-admin.
"""
conversations(
"""
Whether or not to show expired conversations.
"""
includeExpired: Boolean! = false
): HumanConversationConnection!
}
type HumanConversationConnection {
edges: [HumanConversationEdge!]!
nodes: [Conversation!]!
pageInfo: PageInfo!
}
type ConversationPlaybackMarker {
"""
This is the id of the echo that the human last played within this conversation.
Serves as a playback marker for the human to enable 'continue playing a youtube video'.
"""
echoId: String
"""
This is how far we are on the current marker echo. Used for precision.
If we are at the end, will start playing the next echo.
Similar to [MDN audio currentTime](https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/currentTime)
"""
currentPositionSeconds: Int
# """
# Whether or not the current marker is at the latest echo in the conversation.
# """
# FIXME: want data from echoes resolver to update this upstream resolver...how? consolidate resolvers?
# isLatestEcho: Boolean!
}
type HumanConversationEdge {
cursor: String!
node: Conversation!
"""
A set of properties for seamless playback experience for a human in a conversation.
"""
playbackMarker: ConversationPlaybackMarker!
}
type Conversation {
id: ID!
"""
All members of a conversation *excluding* the viewer.
"""
members: ConversationMemberConnection!
"""
Echoes for this conversation, sorted by createdAt ASC.
"""
echoes(
"""
Whether or not to show all echoes for this conversation.
An echo is considered expired if it was created more than 48 hours ago.
"""
includeExpired: Boolean! = false
): EchoConnection!
"""
The last time that an echo, radio, or chat happened in this conversation.
"""
lastActivityAt: Time
"""
A conversation is considered expired if the last activity was more than 48 hours ago.
"""
isExpired: Boolean!
}
type ConversationMemberConnection {
edges: [ConversationMemberEdge!]!
# NOTE: be careful to not to trigger infinite fetch here.
nodes: [Human!]!
pageInfo: PageInfo!
}
type ConversationMemberEdge {
cursor: String!
node: Human!
}
type EchoConnection {
edges: [EchoEdge!]!
nodes: [Echo!]!
pageInfo: PageInfo!
# lastEchoTranscript: String
}
type EchoEdge {
cursor: ID!
node: Echo!
}
enum EchoType {
AUDIO_ONLY
VIDEO
SCREEN
}
enum EchoState {
DRAFT
"""
The creator has committed this to be sent to a conversation when complete.
"""
COMMITTED
"""
Transcribing, transcoding and other activities in progress.
"""
PREPARING
"""
Everything ready for playback experience, going to send soon.
"""
READY
"""
The echo is sent within a conversation.
"""
SENT
"""
The echo failed in some way.
"""
FAILURE
}
type Echo {
id: ID!
createdBy: Human!
createdByViewer: Boolean!
createdAt: Time!
echoType: EchoType!
echoState: EchoState!
"""
URL to download the asset. Will be a temporary pre-signed URL.
"""
assetUrl: URL!
"""
HLS streal URL for asset. Only available if the echo is in 'SENT' state.
"""
streamUrl: URL
"""
Thumbnail image for the asset. Only available if the echo is in 'SENT' state.
"""
thumbnailUrl: URL
"""
Thumbnail gif for the asset. Only available if the echo is in 'SENT' state.
"""
thumbnailGifUrl: URL
"""
Duration in seconds. Only available if the echo is in 'SENT' state.
"""
durationSeconds: Float
"""
The conversation that this echo belongs to.
Only available if the echo is past the 'DRAFT' state.
"""
conversation: Conversation
"""
The transcript of the echo. Only available if the echo is in 'SENT' state.
"""
transcript: Transcript
}
type TranscriptWord {
word: String!
start: Float!
end: Float!
}
type Transcript {
fullText: String!
words: [TranscriptWord!]!
}
type IconName {
value: String!
}
type IconUrl {
value: URL!
}
union VariableIcon = IconName | IconUrl
type KeyTypeKeypress {
mainKey: String!
shiftKey: Boolean!
commandKey: Boolean!
optionKey: Boolean!
controlKey: Boolean!
}
type KeyTypeOpenUrl {
url: URL!
newWindow: Boolean!
}
type KeyTypeOpenApp {
appName: String!
}
type KeyTypeTextSnippet {
text: String!
pasteInCurrentInput: Boolean!
copyToClipboard: Boolean!
}
type KeyTypeTerminalCommand {
command: String!
directory: String!
}
type KeyTypeWriteForMe {
prependedInstruction: String!
"""
e.g. casual, friendly, formal, concise (or any combination)
"""
writingStyle: String!
"""
Whether or not to format for the current app (e.g. Notion in markdown vs. email formatting differences)
Default: true.
"""
formatForCurrentApp: Boolean!
"""
Optional. Provides more context of what I am doing and reading.
"""
attachScreenshot: Boolean!
}
type KeyTypeTypeForMe {
"""
Help with breaking sentences apart and small grammar rules.
"""
minimallyProcess: Boolean!
"""
Whether or not to format for the current app (e.g. Notion in markdown vs. email formatting differences)
Default: true.
"""
formatForCurrentApp: Boolean!
}
type KeyTypeFlowyChat {
prependedInstruction: String!
}
enum MacOsCommandType {
INCREASE_BRIGHTNESS
DECREASE_BRIGHTNESS
MUTE_VOLUME
INCREASE_VOLUME
DECREASE_VOLUME
PLAY_PAUSE
NEXT_TRACK
PREVIOUS_TRACK
DO_NOT_DISTURB
LOCK_SCREEN
LOG_OUT
SLEEP
SHUT_DOWN
RESTART
MISSION_CONTROL
APP_DRAWER
SPOTLIGHT
"""
MacOS: + SHIFT + 3
"""
SCREENSHOT_SCREEN
"""
MacOS: + SHIFT + 4
"""
SCREENSHOT_AREA_SELECT
"""
MacOS: + SHIFT + 5
"""
SCREENSHOT_FULL_OPTIONS
}
type KeyTypeMacOsCommand {
commandType: MacOsCommandType!
}
union KeyConfiguration = KeyTypeKeypress | KeyTypeOpenUrl | KeyTypeOpenApp | KeyTypeTextSnippet | KeyTypeTerminalCommand | KeyTypeWriteForMe | KeyTypeTypeForMe | KeyTypeFlowyChat | KeyTypeMacOsCommand
type Key {
id: ID!
title: String!
position: Int!
hexColor: String
icon: VariableIcon
createdAt: Time!
keyConfiguration: KeyConfiguration!
}
type Space {
id: ID!
name: String!
keys: [Key!]!
createdAt: Time!
icon: VariableIcon
trigger: String
}
type SpaceConnection {
edges: [SpaceEdge!]!
nodes: [Space!]!
pageInfo: PageInfo!
}
type SpaceEdge {
cursor: ID!
node: Space!
}
Binary file not shown.
+88
View File
@@ -0,0 +1,88 @@
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h>
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif
#endif
/* Start of preamble from import "C" comments. */
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
#ifdef _MSC_VER
#include <complex.h>
typedef _Fcomplex GoComplex64;
typedef _Dcomplex GoComplex128;
#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
#endif
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
extern char* QueryGraphQL(char* query);
/* Return type for GetWaitlist */
struct GetWaitlist_return {
char** r0;
long r1;
};
extern struct GetWaitlist_return GetWaitlist();
#ifdef __cplusplus
}
#endif
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
set -e
rm -rf build
mkdir build && cd build
cmake ..
make
cd ..
./build/flowy_admin
cp ./build/compile_commands.json ./
@@ -0,0 +1,138 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// WARNING! Do not edit this file manually, your changes will be overwritten.
#include "ClientClient.h"
#include "graphqlservice/internal/SortedMap.h"
#include <algorithm>
#include <array>
#include <sstream>
#include <stdexcept>
#include <string_view>
#include <utility>
using namespace std::literals;
namespace graphql::client {
namespace graphql {
const std::string& GetRequestText() noexcept
{
static const auto s_request = R"gql(
query GetWaitlist {
getWaitlist {
email
firstName
lastName
}
}
)gql"s;
return s_request;
}
const peg::ast& GetRequestObject() noexcept
{
static const auto s_request = []() noexcept {
auto ast = peg::parseString(GetRequestText());
// This has already been validated against the schema by clientgen.
ast.validated = true;
return ast;
}();
return s_request;
}
} // namespace graphql
using namespace graphql;
template <>
query::GetWaitlist::Response::getWaitlist_WaitlistEntry Response<query::GetWaitlist::Response::getWaitlist_WaitlistEntry>::parse(response::Value&& response)
{
query::GetWaitlist::Response::getWaitlist_WaitlistEntry result;
if (response.type() == response::Type::Map)
{
auto members = response.release<response::MapType>();
for (auto& member : members)
{
if (member.first == R"js(email)js"sv)
{
result.email = ModifiedResponse<std::string>::parse(std::move(member.second));
continue;
}
if (member.first == R"js(firstName)js"sv)
{
result.firstName = ModifiedResponse<std::string>::parse<TypeModifier::Nullable>(std::move(member.second));
continue;
}
if (member.first == R"js(lastName)js"sv)
{
result.lastName = ModifiedResponse<std::string>::parse<TypeModifier::Nullable>(std::move(member.second));
continue;
}
}
}
return result;
}
namespace query::GetWaitlist {
const std::string& GetOperationName() noexcept
{
static const auto s_name = R"gql(GetWaitlist)gql"s;
return s_name;
}
Response parseResponse(response::Value&& response)
{
Response result;
if (response.type() == response::Type::Map)
{
auto members = response.release<response::MapType>();
for (auto& member : members)
{
if (member.first == R"js(getWaitlist)js"sv)
{
result.getWaitlist = ModifiedResponse<query::GetWaitlist::Response::getWaitlist_WaitlistEntry>::parse<TypeModifier::List>(std::move(member.second));
continue;
}
}
}
return result;
}
[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept
{
return graphql::GetRequestText();
}
[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept
{
return graphql::GetRequestObject();
}
[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept
{
return GetWaitlist::GetOperationName();
}
[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response)
{
return GetWaitlist::parseResponse(std::move(response));
}
} // namespace query::GetWaitlist
} // namespace graphql::client
@@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// WARNING! Do not edit this file manually, your changes will be overwritten.
#pragma once
#ifndef CLIENTCLIENT_H
#define CLIENTCLIENT_H
#include "graphqlservice/GraphQLClient.h"
#include "graphqlservice/GraphQLParse.h"
#include "graphqlservice/GraphQLResponse.h"
#include "graphqlservice/internal/Version.h"
// Check if the library version is compatible with clientgen 4.5.0
static_assert(graphql::internal::MajorVersion == 4, "regenerate with clientgen: major version mismatch");
static_assert(graphql::internal::MinorVersion == 5, "regenerate with clientgen: minor version mismatch");
#include <optional>
#include <string>
#include <vector>
namespace graphql::client {
/// <summary>
/// Operation: query GetWaitlist
/// </summary>
/// <code class="language-graphql">
/// query GetWaitlist {
/// getWaitlist {
/// email
/// firstName
/// lastName
/// }
/// }
/// </code>
namespace graphql {
// Return the original text of the request document.
[[nodiscard("unnecessary call")]] const std::string& GetRequestText() noexcept;
// Return a pre-parsed, pre-validated request object.
[[nodiscard("unnecessary call")]] const peg::ast& GetRequestObject() noexcept;
} // namespace graphql
namespace query::GetWaitlist {
using graphql::GetRequestText;
using graphql::GetRequestObject;
// Return the name of this operation in the shared request document.
[[nodiscard("unnecessary call")]] const std::string& GetOperationName() noexcept;
struct [[nodiscard("unnecessary construction")]] Response
{
struct [[nodiscard("unnecessary construction")]] getWaitlist_WaitlistEntry
{
std::string email {};
std::optional<std::string> firstName {};
std::optional<std::string> lastName {};
};
std::vector<getWaitlist_WaitlistEntry> getWaitlist {};
};
[[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response);
struct Traits
{
[[nodiscard("unnecessary call")]] static const std::string& GetRequestText() noexcept;
[[nodiscard("unnecessary call")]] static const peg::ast& GetRequestObject() noexcept;
[[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept;
using Response = GetWaitlist::Response;
[[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response);
};
} // namespace query::GetWaitlist
} // namespace graphql::client
#endif // CLIENTCLIENT_H
+32
View File
@@ -0,0 +1,32 @@
#include <cxxopts/cxxopts.hpp>
#include <iostream>
#include <libgraphql.h>
#include <string>
int main(int argc, char *argv[]) {
cxxopts::Options options("MyCLI", "Example CLI tool");
options.add_options()("n,name", "Your name", cxxopts::value<std::string>());
auto result = options.parse(argc, argv);
if (result.count("name")) {
std::cout << "Hello, " << result["name"].as<std::string>() << "!"
<< std::endl;
} else {
std::cout << "Hello, World!" << std::endl;
}
std::string query = R"(
query {
hello
}
)";
char *response = QueryGraphQL((char *)query.c_str());
std::cout << response << std::endl;
auto waitlistResponse = GetWaitlist();
std::cout << waitlistResponse.r0 << std::endl;
std::cout << waitlistResponse.r1 << std::endl;
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
query GetWaitlist {
getWaitlist {
email
firstName
lastName
}
}
+861
View File
@@ -0,0 +1,861 @@
scalar URL
scalar Time
scalar Upload
enum ErrorCode {
NOT_REGISTERED
ALREADY_REGISTERED
INVALID_EMAIL
INVALID_CODE
BAD_USER_INPUT
UNAUTHENTICATED
FORBIDDEN
NOT_FOUND
INTERNAL_SERVER_ERROR
SIGNIN_CODE_EXPIRED_OR_NOT_FOUND
}
type Query {
"""
Roles: flowy-admin
"""
getWaitlist: [WaitlistEntry!]!
"""
Temporary public way to pull all humans.
"""
humans: [Human!]!
"""
"""
human(id: ID!): Human!
"""
[Temporary] Must set 'Authorization' header to auth-token.
"""
viewer: Human!
"""
Search for humans including contacts by their display name, email.
"""
searchHumans(query: String!): [Human!]!
"""
Returns all of the spaces for the authed human.
"""
spaces: SpaceConnection!
}
type Subscription {
"""
Subscribes to new echoes relevant to the authed human. This means the echo is in 'SENT' state.
Includes echoes sent by the authed human.
"""
echoSent: Echo!
"""
Notifies authed human when relevant echo is expired.
"""
echoExpired(echoId: ID!): Echo!
"""
New conversation created which involves the authed human.
"""
newConversation: ID!
"""
Watch the basic online status of a human.
"""
humanStatus(humanId: ID!): Boolean!
"""
Watches for signals for people echoing in a conversation.
"""
echoSignal(conversationId: ID!): ID!
"""
Receive instructions (for the chrome extension) sent by given human on the keypad.
"""
instruction(humanId: ID!): Instruction!
"""
Receive messages from the chrome extension.
"""
peripheralMessage: PeripheralMessage!
}
type OpenUrlInstruction {
url: URL!
}
type ChatGptFollowUpInstruction {
query: String!
}
type ChatGptToggleWebSearchInstruction {
# FIX: required to have one field for some reason
fakeField: Boolean
}
type GoogleCalendarUpdateViewInstruction {
hotkey: String!
}
type VariableInstruction {
"""
Will be a json representation of a instruction defined in keypad & chrome extension.
"""
message: String!
}
union Instruction = OpenUrlInstruction | ChatGptFollowUpInstruction | ChatGptToggleWebSearchInstruction | GoogleCalendarUpdateViewInstruction | VariableInstruction
type UrlUpdatedMessage {
currentUrl: URL!
}
type StateUpdateMessage {
url: URL!
"""
Json representation for specific application context.
"""
state: String
}
union PeripheralMessage = UrlUpdatedMessage | StateUpdateMessage
type Mutation {
"""
Registers a new human.
Errors if already registered.
"""
register(email: String!, displayName: String!): Boolean!
"""
Sends email with a code.
ErrorCodeNotFound if not registered.
"""
signIn(email: String!): Boolean!
"""
Verifies the code and returns a token.
ErrorCodeNotFound if not registered.
ErrorSignInCodeExpiredOrNotFound if the code is expired or not found.
"""
signInVerify(email: String!, code: String!): TokenOutput!
"""
If already in the waitlist, gracefully returns success.
Roles: public
"""
joinWaitlist(input: JoinWaitlistInput!): Boolean!
"""
Add a human into the system.
Returns the human.
"""
addHuman(input: AddHumanInput!): Human!
"""
The first step in flowy.llink (thought).
Returns ID of the draft echo.
"""
draftEcho(input: DraftEchoInput!): DraftEchoOutput!
"""
The second step in flowy.llink (top-of-mind people).
Requires authentication as the human who drafted.
The system will prepare and send the echo after this step and you will be notified in echoReceived subscription.
Returns true if successful.
"""
commitEcho(input: CommitEchoInput!): Boolean!
"""
Adds a human to a conversation.
Requires authentication as the admin of the conversation.
"""
addHumanToConversation(input: AddHumanToConversationInput!): Boolean!
"""
Updates the profile picture of the authed human.
"""
updateHumanProfile(input: UpdateHumanProfileInput!): Boolean!
"""
Updates the playback marker for the authed human in a conversation.
Must be a member of the conversation.
"""
updatePlaybackMarker(conversationId: ID!, echoId: ID!, currentPositionSeconds: Int!): ConversationPlaybackMarker!
"""
Updates the last seen time of the authed human.
"""
updateLastSeen: Boolean!
"""
Updates the presence of the authed human.
"""
disconnectPresence: Boolean!
"""
Must be a member of the conversation.
Signals to others in the conversation that the authed human is echoing.
"""
sendEchoSignal(conversationId: ID!): Boolean!
"""
Generates a token to join the live room.
"""
generateLiveToken(conversationId: ID!): String!
"""
Sends instruction to chrome extension.
"""
sendInstruction(input: SendInstructionInput!): Boolean!
"""
For sending responses/messages from the chrome extension to the keypad.
"""
sendMessageToKeypad(input: SendMessageToKeypadInput!): Boolean!
addSpace(input: AddSpaceInput!): Space!
updateSpace(input: UpdateSpaceInput!): Space!
moveSpace(input: MoveSpaceInput!): Boolean!
deleteSpace(input: DeleteSpaceInput!): Boolean!
setKey(input: SetKeyInput!): Key!
swapKeys(input: SwapKeysInput!): Boolean!
deleteKey(input: DeleteKeyInput!): Boolean!
}
input AddSpaceInput {
name: String!
"""
Optional. Either set this or `iconUrl`, not both.
"""
iconName: String
"""
Optional. Either set this or `iconName`, not both.
"""
iconUrl: URL
"""
Optional. Either specify a domain or an app name followed by the extension
(e.g. 'app:Visual Studio Code' or 'url_contains:flowy.com' or 'url_contains:youtube.com/watch').
"""
trigger: String
}
input UpdateSpaceInput {
spaceId: ID!
name: String!
"""
Optional. Either set this or `iconUrl`, not both.
"""
iconName: String
"""
Optional. Either set this or `iconName`, not both.
"""
iconUrl: URL
"""
Optional. Either specify a domain or an app name followed by the extension
(e.g. 'app:Visual Studio Code' or 'url_contains:flowy.com' or 'url_contains:youtube.com/watch').
"""
trigger: String
}
enum MoveSpaceDirection {
UP
DOWN
}
input MoveSpaceInput {
spaceId: ID!
direction: MoveSpaceDirection!
}
input DeleteSpaceInput {
spaceId: ID!
}
input KeyTypeKeypressInput {
mainKey: String!
shiftKey: Boolean!
commandKey: Boolean!
optionKey: Boolean!
controlKey: Boolean!
}
input KeyTypeOpenUrlInput {
url: String! # URL is typically represented as a String in GraphQL
newWindow: Boolean!
}
input KeyTypeOpenAppInput {
appName: String!
}
input KeyTypeTextSnippetInput {
text: String!
pasteInCurrentInput: Boolean!
copyToClipboard: Boolean!
}
input KeyTypeTerminalCommandInput {
command: String!
directory: String!
}
input KeyTypeWriteForMeInput {
prependedInstruction: String!
writingStyle: String
formatForCurrentApp: Boolean!
attachScreenshot: Boolean!
}
input KeyTypeTypeForMeInput {
minimallyProcess: Boolean!
formatForCurrentApp: Boolean!
}
input KeyTypeFlowyChatInput {
prependedInstruction: String!
}
input KeyTypeMacOsCommandInput {
commandType: MacOsCommandType!
}
input KeyConfigurationInput {
keypress: KeyTypeKeypressInput
openUrl: KeyTypeOpenUrlInput
openApp: KeyTypeOpenAppInput
textSnippet: KeyTypeTextSnippetInput
terminalCommand: KeyTypeTerminalCommandInput
writeForMe: KeyTypeWriteForMeInput
typeForMe: KeyTypeTypeForMeInput
flowyChat: KeyTypeFlowyChatInput
macOsCommand: KeyTypeMacOsCommandInput
}
input SetKeyInput {
spaceId: ID!
position: Int!
title: String!
"""
Optional. Either set this or `iconUrl`, not both.
"""
iconName: String
"""
Optional. Either set this or `iconName`, not both.
"""
iconUrl: URL
hexColor: String
"""
Must specify one of the inputs.
"""
keyConfiguration: KeyConfigurationInput!
}
input SwapKeysInput {
spaceId: ID!
positionA: Int!
positionB: Int!
}
input DeleteKeyInput {
spaceId: ID!
position: Int!
}
enum Role {
ADMIN,
HUMAN,
}
type TokenOutput {
token: String!
role: Role!
}
input UrlUpdatedMessageInput {
"""
Current url of the active tab and focused window.
"""
currentUrl: URL!
}
input StateUpdateInput {
url: URL!
"""
State of specific site in url in json form.
"""
state: String
}
input SendMessageToKeypadInput {
humanId: ID!
urlUpdated: UrlUpdatedMessageInput
stateUpdate: StateUpdateInput
}
input SendInstructionUpdateGoogleCalendarViewInput {
"""
The hotkey to update the google calendar view.
"""
hotkey: String!
}
input SendInstructionOpenUrlInput {
url: URL!
}
input SendInstructionChatGptFollowUpInput {
query: String!
}
input SendInstructionChatGptToggleWebSearchInput {
# FIX: required to have one field for some reason
fakeField: Boolean
}
input SendVariableInstructionInput {
"""
Will be a json representation of a instruction defined in keypad & chrome extension.
"""
message: String!
}
### FIX: dealing with polymorphism here by having different optional fields because can't have a union in an input type
input SendInstructionInput {
openUrl: SendInstructionOpenUrlInput
chatGptFollowUp: SendInstructionChatGptFollowUpInput
chatGptToggleWebSearch: SendInstructionChatGptToggleWebSearchInput
updateGoogleCalendarView: SendInstructionUpdateGoogleCalendarViewInput
variable: SendVariableInstructionInput
}
type Header {
key: String!
value: String!
}
type DraftEchoOutput {
draftEchoId: ID!
"""
The URL to upload the asset to. Must be done before committing the echo.
"""
assetUploadUrl: URL!
"""
Use these headers to upload the asset to the given URL.
"""
uploadHeaders: [Header!]!
}
enum CompanyWorkspaceSuite {
GSuite
Office365
}
type WaitlistEntry {
email: String!
firstName: String
lastName: String
companyWebsite: URL
companyWorkspaceSuite: CompanyWorkspaceSuite
isDecisionMaker: Boolean
}
input JoinWaitlistInput {
email: String!
firstName: String
lastName: String
companyWebsite: URL
companyWorkspaceSuite: CompanyWorkspaceSuite
isDecisionMaker: Boolean
}
input AddHumanInput {
email: String!
displayName: String!
profilePicture: Upload
}
input DraftEchoInput {
contentLength: Int!
contentType: String!
"""
Must match the mime type of the asset. If `video/*`, then you can use `VIDEO` or `SCREEN`.
"""
echoType: EchoType!
}
input CommitEchoInput {
draftEchoId: ID!
"""
Send to the given existing conversation.
Authed human must be a member of the conversation.
Must have this or `newConversation`.
"""
existingConversation: String
"""
Send to a new conversation with the given human ids.
Authed human becomes the admin of the conversation.
Must have this or `existingConversation`.
Do NOT include the echo creator in the list.
"""
newConversation: [ID!]
}
input AddHumanToConversationInput {
conversationId: ID!
humanId: ID!
}
input UpdateHumanProfileInput {
"""
The profile picture to update. Optional.
If kept empty, will retain the former profile picture.
"""
profilePicture: Upload
"""
The display name to update. Optional.
If kept empty, will retain the former display name.
"""
displayName: String
}
"""
Orders by all types DESC
"""
enum PaginationOrderBy {
CREATED_AT
UPDATED_AT
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
type Human {
id: ID!
email: String!
displayName: String!
profilePictureUrl: URL
"""
Two letters based on the display name.
"""
initials: String!
isFlowyAdmin: Boolean!
createdAt: Time!
"""
Conversations that the human is involved.
Only available if the viewer is this human or flowy-admin.
"""
conversations(
"""
Whether or not to show expired conversations.
"""
includeExpired: Boolean! = false
): HumanConversationConnection!
}
type HumanConversationConnection {
edges: [HumanConversationEdge!]!
nodes: [Conversation!]!
pageInfo: PageInfo!
}
type ConversationPlaybackMarker {
"""
This is the id of the echo that the human last played within this conversation.
Serves as a playback marker for the human to enable 'continue playing a youtube video'.
"""
echoId: String
"""
This is how far we are on the current marker echo. Used for precision.
If we are at the end, will start playing the next echo.
Similar to [MDN audio currentTime](https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/currentTime)
"""
currentPositionSeconds: Int
# """
# Whether or not the current marker is at the latest echo in the conversation.
# """
# FIXME: want data from echoes resolver to update this upstream resolver...how? consolidate resolvers?
# isLatestEcho: Boolean!
}
type HumanConversationEdge {
cursor: String!
node: Conversation!
"""
A set of properties for seamless playback experience for a human in a conversation.
"""
playbackMarker: ConversationPlaybackMarker!
}
type Conversation {
id: ID!
"""
All members of a conversation *excluding* the viewer.
"""
members: ConversationMemberConnection!
"""
Echoes for this conversation, sorted by createdAt ASC.
"""
echoes(
"""
Whether or not to show all echoes for this conversation.
An echo is considered expired if it was created more than 48 hours ago.
"""
includeExpired: Boolean! = false
): EchoConnection!
"""
The last time that an echo, radio, or chat happened in this conversation.
"""
lastActivityAt: Time
"""
A conversation is considered expired if the last activity was more than 48 hours ago.
"""
isExpired: Boolean!
}
type ConversationMemberConnection {
edges: [ConversationMemberEdge!]!
# NOTE: be careful to not to trigger infinite fetch here.
nodes: [Human!]!
pageInfo: PageInfo!
}
type ConversationMemberEdge {
cursor: String!
node: Human!
}
type EchoConnection {
edges: [EchoEdge!]!
nodes: [Echo!]!
pageInfo: PageInfo!
# lastEchoTranscript: String
}
type EchoEdge {
cursor: ID!
node: Echo!
}
enum EchoType {
AUDIO_ONLY
VIDEO
SCREEN
}
enum EchoState {
DRAFT
"""
The creator has committed this to be sent to a conversation when complete.
"""
COMMITTED
"""
Transcribing, transcoding and other activities in progress.
"""
PREPARING
"""
Everything ready for playback experience, going to send soon.
"""
READY
"""
The echo is sent within a conversation.
"""
SENT
"""
The echo failed in some way.
"""
FAILURE
}
type Echo {
id: ID!
createdBy: Human!
createdByViewer: Boolean!
createdAt: Time!
echoType: EchoType!
echoState: EchoState!
"""
URL to download the asset. Will be a temporary pre-signed URL.
"""
assetUrl: URL!
"""
HLS streal URL for asset. Only available if the echo is in 'SENT' state.
"""
streamUrl: URL
"""
Thumbnail image for the asset. Only available if the echo is in 'SENT' state.
"""
thumbnailUrl: URL
"""
Thumbnail gif for the asset. Only available if the echo is in 'SENT' state.
"""
thumbnailGifUrl: URL
"""
Duration in seconds. Only available if the echo is in 'SENT' state.
"""
durationSeconds: Float
"""
The conversation that this echo belongs to.
Only available if the echo is past the 'DRAFT' state.
"""
conversation: Conversation
"""
The transcript of the echo. Only available if the echo is in 'SENT' state.
"""
transcript: Transcript
}
type TranscriptWord {
word: String!
start: Float!
end: Float!
}
type Transcript {
fullText: String!
words: [TranscriptWord!]!
}
type IconName {
value: String!
}
type IconUrl {
value: URL!
}
union VariableIcon = IconName | IconUrl
type KeyTypeKeypress {
mainKey: String!
shiftKey: Boolean!
commandKey: Boolean!
optionKey: Boolean!
controlKey: Boolean!
}
type KeyTypeOpenUrl {
url: URL!
newWindow: Boolean!
}
type KeyTypeOpenApp {
appName: String!
}
type KeyTypeTextSnippet {
text: String!
pasteInCurrentInput: Boolean!
copyToClipboard: Boolean!
}
type KeyTypeTerminalCommand {
command: String!
directory: String!
}
type KeyTypeWriteForMe {
prependedInstruction: String!
"""
e.g. casual, friendly, formal, concise (or any combination)
"""
writingStyle: String!
"""
Whether or not to format for the current app (e.g. Notion in markdown vs. email formatting differences)
Default: true.
"""
formatForCurrentApp: Boolean!
"""
Optional. Provides more context of what I am doing and reading.
"""
attachScreenshot: Boolean!
}
type KeyTypeTypeForMe {
"""
Help with breaking sentences apart and small grammar rules.
"""
minimallyProcess: Boolean!
"""
Whether or not to format for the current app (e.g. Notion in markdown vs. email formatting differences)
Default: true.
"""
formatForCurrentApp: Boolean!
}
type KeyTypeFlowyChat {
prependedInstruction: String!
}
enum MacOsCommandType {
INCREASE_BRIGHTNESS
DECREASE_BRIGHTNESS
MUTE_VOLUME
INCREASE_VOLUME
DECREASE_VOLUME
PLAY_PAUSE
NEXT_TRACK
PREVIOUS_TRACK
DO_NOT_DISTURB
LOCK_SCREEN
LOG_OUT
SLEEP
SHUT_DOWN
RESTART
MISSION_CONTROL
APP_DRAWER
SPOTLIGHT
"""
MacOS: + SHIFT + 3
"""
SCREENSHOT_SCREEN
"""
MacOS: + SHIFT + 4
"""
SCREENSHOT_AREA_SELECT
"""
MacOS: + SHIFT + 5
"""
SCREENSHOT_FULL_OPTIONS
}
type KeyTypeMacOsCommand {
commandType: MacOsCommandType!
}
union KeyConfiguration = KeyTypeKeypress | KeyTypeOpenUrl | KeyTypeOpenApp | KeyTypeTextSnippet | KeyTypeTerminalCommand | KeyTypeWriteForMe | KeyTypeTypeForMe | KeyTypeFlowyChat | KeyTypeMacOsCommand
type Key {
id: ID!
title: String!
position: Int!
hexColor: String
icon: VariableIcon
createdAt: Time!
keyConfiguration: KeyConfiguration!
}
type Space {
id: ID!
name: String!
keys: [Key!]!
createdAt: Time!
icon: VariableIcon
trigger: String
}
type SpaceConnection {
edges: [SpaceEdge!]!
nodes: [Space!]!
pageInfo: PageInfo!
}
type SpaceEdge {
cursor: ID!
node: Space!
}