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!
}
