<commit_before>import MsgCtrl from './ctrl';
import { MsgData, Contact, User, Msg, Convo, SearchResult } from './interfaces';

const headers: HeadersInit = {
  'Accept': 'application/vnd.lichess.v5+json'
};
const cache: RequestCache = 'no-cache';

export function loadConvo(userId: string): Promise<MsgData> {
  return fetch(`/inbox/${userId}`, { headers, cache })
    .then(r => r.json())
    .then(upgradeData);
}

export function loadContacts(): Promise<MsgData> {
  return fetch(`/inbox`, { headers, cache })
    .then(r => r.json())
    .then(upgradeData);
}

export function search(q: string): Promise<SearchResult> {
  return fetch(`/inbox/search?q=${q}`)
    .then(r => r.json())
    .then(res => ({
      ...res,
      contacts: res.contacts.map(upgradeContact)
    } as SearchResult));
}

export function block(u: string) {
  return fetch(`/rel/block/${u}`, {
    method: 'post',
    headers
  });
}

export function unblock(u: string) {
  return fetch(`/rel/unblock/${u}`, {
    method: 'post',
    headers
  });
}

export function del(u: string): Promise<MsgData> {
  return fetch(`/inbox/${u}`, {
    method: 'delete',
    headers
  })
    .then(r => r.json())
    .then(upgradeData);
}

export function report(name: string, text: string): Promise<any> {
  const formData = new FormData()
  formData.append('username', name);
  formData.append('text', text);
  formData.append('resource', 'msg');
  return fetch('/report/flag', {
    method: 'post',
    headers,
    body: formData
  });
}

export function post(dest: string, text: string) {
  window.lichess.pubsub.emit('socket.send', 'msgSend', { dest, text });
}

export function setRead(dest: string) {
  window.lichess.pubsub.emit('socket.send', 'msgRead', dest);
}

export function websocketHandler(ctrl: MsgCtrl) {
  const listen = window.lichess.pubsub.on;
  listen('socket.in.msgNew', msg => {
    ctrl.receive({
      ...upgradeMsg(msg),
      read: false
    });
  });
  listen('socket.in.blockedBy', ctrl.changeBlockBy);
  listen('socket.in.unblockedBy', ctrl.changeBlockBy);

  let connected = true;
  listen('socket.close', () => {
    connected = false;
    ctrl.redraw();
  });
  listen('socket.open', () => {
    if (!connected) {
      connected = true;
      ctrl.onReconnect();
    }
  });

  return () => connected;
}

// the upgrade functions convert incoming timestamps into JS dates
export function upgradeData(d: any): MsgData {
  return {
    ...d,
    convo: d.convo && upgradeConvo(d.convo),
    contacts: d.contacts.map(upgradeContact)
  };
}
function upgradeMsg(m: any): Msg {
  return {
    ...m,
    date: new Date(m.date)
  };
}<commit_msg>better handle fetch responses<commit_after>import MsgCtrl from './ctrl';
import { MsgData, Contact, User, Msg, Convo, SearchResult } from './interfaces';

const headers: HeadersInit = {
  'Accept': 'application/vnd.lichess.v5+json'
};
const cache: RequestCache = 'no-cache';

export function loadConvo(userId: string): Promise<MsgData> {
  return fetch(`/inbox/${userId}`, { headers, cache })
    .then(httpResponse)
    .then(upgradeData);
}

export function loadContacts(): Promise<MsgData> {
  return fetch(`/inbox`, { headers, cache })
    .then(httpResponse)
    .then(upgradeData);
}

export function search(q: string): Promise<SearchResult> {
  return fetch(`/inbox/search?q=${q}`)
    .then(httpResponse)
    .then(res => ({
      ...res,
      contacts: res.contacts.map(upgradeContact)
    } as SearchResult));
}

export function block(u: string) {
  return fetch(`/rel/block/${u}`, {
    method: 'post',
    headers
  });
}

export function unblock(u: string) {
  return fetch(`/rel/unblock/${u}`, {
    method: 'post',
    headers
  });
}

export function del(u: string): Promise<MsgData> {
  return fetch(`/inbox/${u}`, {
    method: 'delete',
    headers
  })
    .then(httpResponse)
    .then(upgradeData);
}

export function report(name: string, text: string): Promise<any> {
  const formData = new FormData()
  formData.append('username', name);
  formData.append('text', text);
  formData.append('resource', 'msg');
  return fetch('/report/flag', {
    method: 'post',
    headers,
    body: formData
  });
}

export function post(dest: string, text: string) {
  window.lichess.pubsub.emit('socket.send', 'msgSend', { dest, text });
}

export function setRead(dest: string) {
  window.lichess.pubsub.emit('socket.send', 'msgRead', dest);
}

export function websocketHandler(ctrl: MsgCtrl) {
  const listen = window.lichess.pubsub.on;
  listen('socket.in.msgNew', msg => {
    ctrl.receive({
      ...upgradeMsg(msg),
      read: false
    });
  });
  listen('socket.in.blockedBy', ctrl.changeBlockBy);
  listen('socket.in.unblockedBy', ctrl.changeBlockBy);

  let connected = true;
  listen('socket.close', () => {
    connected = false;
    ctrl.redraw();
  });
  listen('socket.open', () => {
    if (!connected) {
      connected = true;
      ctrl.onReconnect();
    }
  });

  return () => connected;
}

function httpResponse(response: Response) {
  if (!response.ok) return response.json();
  alert(response.statusText);
  throw response.statusText;
}

// the upgrade functions convert incoming timestamps into JS dates
export function upgradeData(d: any): MsgData {
  return {
    ...d,
    convo: d.convo && upgradeConvo(d.convo),
    contacts: d.contacts.map(upgradeContact)
  };
}
function upgradeMsg(m: any): Msg {
  return {
    ...m,
    date: new Date(m.date)
  };
}
{'fraction_non_alphanumeric': 0.17280088332719912, 'fraction_numerical': 0.000368052999631947, 'mean_word_length': 3.5787700084245997, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4473854', 'n_tokens_mistral': 1824, 'n_tokens_neox': 1817, 'n_words': 397}