## Core system

- [X] Base structure of JSON API code 
- [X] Implement the first methods for testing + the code that should later be generated by the proc macro
- [X] Create the proc macro
  - [X] json api
  - [X] ts types
    - [X] arguments (no args, one argument, multiple args)
    - [X] return type
    - [X] custom types as type aliases that ts file looks prettier


## Pre - MVP

- [X] Web socket server
- [WIP] Web socket client (ts)
  - [X] backend connection state changed events
  - [X] Reconnect on connection loss / connection state
  - [ ] find a way to type the event emitter callback functions
- [X] Events

## MVP

- [X] mocha integration test for ts api
 - [X] basic tests
 - [X] advanced / "online tests" (mailadm for burner accounts)
- [ ] coverage for a majority of the API
- [ ] Blobs served
- [ ] Blob upload (for attachments, setting profile-picture, importing backup and so on)
- [ ] Web push API? At least some kind of notification hook closure this lib can accept.

## Other Ideas

- [ ] make sure there can only be one connection at a time to the ws 
  - why? , it could give problems if its commanded from multiple connections
- [ ] encrypted connection?
- [ ] authenticated connection?
- [ ] Look into unit-testing for the proc macros?
- [ ] proc macro taking over doc comments to generated typescript file
- [X] GH action for tests (rust and typescript)
  - [X] rust test
  - [X] rust fmt
  - [X] rust clippy
  - [X] tsc check
  - [X] prettier
  - [X] mocha
- [X] scripts to check&fix prettier formatting



## Apis

replicate desktop api feature set:

(this feature set is based on desktop version `1.20`, needs to be updated in the future)

```rs


struct sendMessageParams {
  text: Option<String>,
  filename: Option<String>, // TODO we need to think about blobs some more
  location: Option<(u32,u32)>,
  quote_message_id: Option<u32>,
}

struct QrCodeResponse = {
  state: u32 // also enum in reality, for simlicity u32 here
  id: u32
  text1: String
}

impl Api {

// root ---------------------------------------------------------------

// NEEDS_THE_BLOB_QUESTION_ANSWERED_EVENTUALLY
async fn sc_set_profile_picture(&self, new_image: String) -> Result<()> {}

// NEEDS_THE_BLOB_QUESTION_ANSWERED_EVENTUALLY
// 'getProfilePicture' equals to `dc.getContact(C.DC_CONTACT_ID_SELF).getProfileImage()` or `dc.get_config("selfavatar")`

async fn sc_join_secure_join(&self, qrCode: String) -> Result<u32> {}
async fn sc_stop_ongoing_process(&self) -> Result<u32> {}
async fn sc_check_qr_code(&self, qrCode: String) -> Result<QrCodeResponse> {}

// login ----------------------------------------------------

// INFO: login functions need to call stop&start io where applicable

// login.newLogin:
// do instead in frontend:
//   1. call `add_account`
//   2. call `select_account`
//   3. set credentials via set config
//   4. call `sc_configure`

// login.getLogins - is already implemented: `get_all_accounts`

// login.loadAccount - Basically `select_account`

// login.logout -> TODO: unselect account, which isn't implemented in the core yet

// login.forgetAccount -> `remove_account`

// login.getLastLoggedInAccount -> `get_selected_account_id`

// login.updateCredentials -> do instead: set config then call `sc_configure`

// backup -------------------------------------------------------------

// INFO: backup functions need to call stop&start io

// NEEDS_THE_BLOB_QUESTION_ANSWERED_EVENTUALLY
async fn sc_backup_export(&self, out_dir: String) -> Result<()> {}
// NEEDS_THE_BLOB_QUESTION_ANSWERED_EVENTUALLY
async fn sc_backup_import(&self, file: String) -> Result<()> {} // will not return the same as in desktop because this function imports backup to the current context unlike it was in desktop

// chatList -----------------------------------------------------------

// chatList.selectChat - will be removed from desktop
// chatList.getSelectedChatId - will be removed from desktop
// chatList.onChatModified - will be removed from desktop

async fn sc_chatlist_get_general_fresh_message_counter(&self) -> Result<u32> // this method might be used for a favicon badge counter

// contacts ------------------------------------------------------------

async fn sc_contacts_change_nickname(&self, contact_id: u32, new_name: String) -> Result<()>


// contacts.getChatIdByContactId - very similar to sc_contacts_create_chat_by_contact_id
// contacts.getDMChatId -  very similar to sc_contacts_create_chat_by_contact_id

async fn sc_contacts_get_encryption_info(&self, contact_id: u32) -> Result<String>

async fn sc_contacts_lookup_contact_id_by_addr(&self, email: String) -> Result<u32>


}

```
```ts

class DeltaRemote {
  // chat ---------------------------------------------------------------
  call(
    fnName: 'chat.getChatMedia',
    chatId: number,
    msgType1: number,
    msgType2: number
  ): Promise<MessageType[]>
  call(fnName: 'chat.getEncryptionInfo', chatId: number): Promise<string>
  call(fnName: 'chat.getQrCode', chatId?: number): Promise<string>
  call(fnName: 'chat.leaveGroup', chatId: number): Promise<void>
  call(fnName: 'chat.setName', chatId: number, name: string): Promise<boolean>
  call(
    fnName: 'chat.modifyGroup',
    chatId: number,
    name: string,
    image: string,
    remove: number[],
    add: number[]
  ): Promise<boolean>
  call(
    fnName: 'chat.addContactToChat',
    chatId: number,
    contactId: number
  ): Promise<boolean>
  call(
    fnName: 'chat.setProfileImage',
    chatId: number,
    newImage: string
  ): Promise<boolean>
  call(
    fnName: 'chat.setMuteDuration',
    chatId: number,
    duration: MuteDuration
  ): Promise<boolean>
  call(
    fnName: 'chat.createGroupChat',
    verified: boolean,
    name: string
  ): Promise<number>
  call(fnName: 'chat.delete', chatId: number): Promise<void>
  call(
    fnName: 'chat.setVisibility',
    chatId: number,
    visibility:
      | C.DC_CERTCK_AUTO
      | C.DC_CERTCK_STRICT
      | C.DC_CHAT_VISIBILITY_PINNED
  ): Promise<void>
  call(fnName: 'chat.getChatContacts', chatId: number): Promise<number[]>
  call(fnName: 'chat.markNoticedChat', chatId: number): Promise<void>
  call(fnName: 'chat.getChatEphemeralTimer', chatId: number): Promise<number>
  call(
    fnName: 'chat.setChatEphemeralTimer',
    chatId: number,
    ephemeralTimer: number
  ): Promise<void>
  call(fnName: 'chat.sendVideoChatInvitation', chatId: number): Promise<number>
  call(
    fnName: 'chat.decideOnContactRequest',
    messageId: number,
    decision:
      | C.DC_DECISION_START_CHAT
      | C.DC_DECISION_NOT_NOW
      | C.DC_DECISION_BLOCK
  ): Promise<number>
  // locations ----------------------------------------------------------
  call(
    fnName: 'locations.setLocation',
    latitude: number,
    longitude: number,
    accuracy: number
  ): Promise<void>
  call(
    fnName: 'locations.getLocations',
    chatId: number,
    contactId: number,
    timestampFrom: number,
    timestampTo: number
  ): Promise<JsonLocations>

  // NOTHING HERE that is called directly from the frontend, yet
  // messageList --------------------------------------------------------
  call(
    fnName: 'messageList.sendMessage',
    chatId: number,
    params: sendMessageParams
  ): Promise<[number, MessageType | null]>
  call(
    fnName: 'messageList.sendSticker',
    chatId: number,
    stickerPath: string
  ): Promise<void>
  call(fnName: 'messageList.deleteMessage', id: number): Promise<void>
  call(fnName: 'messageList.getMessageInfo', msgId: number): Promise<string>
  call(
    fnName: 'messageList.getDraft',
    chatId: number
  ): Promise<MessageType | null>
  call(
    fnName: 'messageList.setDraft',
    chatId: number,
    {
      text,
      file,
      quotedMessageId,
    }: { text?: string; file?: string; quotedMessageId?: number }
  ): Promise<void>
  call(
    fnName: 'messageList.messageIdToJson',
    id: number
  ): Promise<{ msg: null } | MessageType>
  call(
    fnName: 'messageList.forwardMessage',
    msgId: number,
    chatId: number
  ): Promise<void>
  call(
    fnName: 'messageList.searchMessages',
    query: string,
    chatId?: number
  ): Promise<number[]>
  call(
    fnName: 'messageList.msgIds2SearchResultItems',
    msgIds: number[]
  ): Promise<{ [id: number]: MessageSearchResult }>
  call(
    fnName: 'messageList.saveMessageHTML2Disk',
    messageId: number
  ): Promise<string>
  // settings -----------------------------------------------------------
  call(fnName: 'settings.keysImport', directory: string): Promise<void>
  call(fnName: 'settings.keysExport', directory: string): Promise<void>
  call(
    fnName: 'settings.serverFlags',
    {
      mail_security,
      send_security,
    }: {
      mail_security?: string
      send_security?: string
    }
  ): Promise<number | ''>
  call(
    fnName: 'settings.setDesktopSetting',
    key: keyof DesktopSettings,
    value: string | number | boolean
  ): Promise<boolean>
  call(fnName: 'settings.getDesktopSettings'): Promise<DesktopSettings>
  call(
    fnName: 'settings.saveBackgroundImage',
    file: string,
    isDefaultPicture: boolean
  ): Promise<string>
  call(
    fnName: 'settings.estimateAutodeleteCount',
    fromServer: boolean,
    seconds: number
  ): Promise<number>
  // stickers -----------------------------------------------------------
  call(
    fnName: 'stickers.getStickers'
  ): Promise<{
    [key: string]: string[]
  }> // todo move to extras? because its not directly elated to core
  // context ------------------------------------------------------------
  call(fnName: 'context.maybeNetwork'): Promise<void>
  // burner accounts ------------------------------------------------------------
  call(
    fnName: 'burnerAccounts.create',
    url: string
  ): Promise<{ email: string; password: string }> // think about how to improve that api - probably use core api instead
  // extras -------------------------------------------------------------
  call(fnName: 'extras.getLocaleData', locale: string): Promise<LocaleData>
  call(fnName: 'extras.setLocale', locale: string): Promise<void>
  call(
    fnName: 'extras.getActiveTheme'
  ): Promise<{
    theme: Theme
    data: string
  } | null>
  call(fnName: 'extras.setThemeFilePath', address: string): void
  call(fnName: 'extras.getAvailableThemes'): Promise<Theme[]>
  call(fnName: 'extras.setTheme', address: string): Promise<boolean>
  // catchall: ----------------------------------------------------------
  call(fnName: string): Promise<any>
  call(fnName: string, ...args: any[]): Promise<any> {
    return _callDcMethodAsync(fnName, ...args)
  }
}

export const DeltaBackend = new DeltaRemote()
```


after that, or while doing it adjust api to be more complete




TODO different test to simulate two devices:

to test autocrypt_initiate_key_transfer & autocrypt_continue_key_transfer