<commit_before>'use strict';

import { getPreferredTitleFromEnsemblTranscriptsNoMatch } from "./parse-resources";

/**
 * Method to return the canonical transcript given the Ensembl transcripts.
 * Note that there are possibly multiple canonical transcript(s) in Ensembl
 * transcripts, this method only picks the representative one, since this method
 * is intended to be used for getting a single canonical transcript title for a variant.
 *
 * Among the canonical transcripts, whether or not one qualifies as the representative
 * canonical transcript, is determined by algorithm below:
 *
 * 1. [**General Singularity Test**] - If only one canonical transcript exists in Ensembl transcripts, return it
 * 2. [**NM Singularity Test**] - If there're multiple canonical transcripts, and only one of them whose
 *      c. nomenclature (i.e. `hgvsc`) starts with NM, return it
 * 3. [**NM Singularity Test**] - If there're multiple canonical transcripts whose `hgvsc` starts with NM,
 *      return null, which also means we don't use canonical transcripts for variant title
 * 4, [**NR Singularity Test**] - If there're no canonical transcript with `NM` prefix, look for canonical transcripts
 *      with `NR` prefix; if there's exactly one with `NR` prefix, return it
 * 5. Otherwise return null; this includes cases of multiple caonical transcripts with NR prefix
 *
 *
 * Examples:
 *
 * e.g. 1: `NR_1, XR_1, XR_2, XM_1, XM_2 ...`: in this case there is no NM but there is one NR_1 so use that
 *
 * e.g. 2: `NR_1, NR_2, XR_1, XR_2, XM_1, XM_2 ...`: in this case skip because there is no NM and there is more than one NR
 *
 * e.g. 3: `NM_1, NR_1, NR_2, XR_1, XR_2, XM_1, XM_2 ...`: in this case use NM_1
 *
 * e.g. 4: `NM_1, NM_2_ NR_1, XR_1, XR_2, XM_1, XM_2 ...`: in this case skip because there is more than one NM
 *
 * @see {@link https://github.com/ClinGen/clincoded/issues/2176|Issue 2176}
 * @see getCanonicalTranscriptTitleFromEnsemblTranscripts
 *
 * @param {array} transcripts - Ensembl transcripts
 * @param {boolean} extended - If `true`, will return an transcript object; otherwise, will just return the hgvsc string of the transcript
 *
 * @returns {(string|object|null|undefined)} Returns the hgvs string of canonical transcript, or the entire transcript object from Ensembl API, depending on argument `extended`.
 *      Will return `null` if no canonical transcript found, or found but no `hgvsc` field on it.
 */
export function getCanonicalTranscript(transcripts, extended = false) {

    // Filter all ensembl transcripts marked as canonical

    const canonicalTranscriptsFromEnsembl = transcripts.filter((transcript) => (
        transcript.hgvsc &&
        // transcript.source === 'RefSeq' &&
        transcript.canonical
    ));

    if (canonicalTranscriptsFromEnsembl.length === 0) {
        return null;
    }

    // General Singularity Test

    if (canonicalTranscriptsFromEnsembl.length === 1) {
        return extended ? canonicalTranscriptsFromEnsembl[0] : canonicalTranscriptsFromEnsembl[0].hgvsc;
    }

    // NM Singularity Test

    const canonicalTranscriptsStartByNM = canonicalTranscriptsFromEnsembl.filter(({hgvsc = ''}) => (typeof hgvsc === 'string' && hgvsc.trim().startsWith('NM')))

    if (canonicalTranscriptsStartByNM.length === 1) {
        return extended ? canonicalTranscriptsStartByNM[0] : canonicalTranscriptsStartByNM[0].hgvsc;
    }

    // NR Singularity Test

    const canonicalTranscriptsStartByNR = canonicalTranscriptsFromEnsembl.filter(({hgvsc = ''}) => (typeof hgvsc === 'string' && hgvsc.trim().startsWith('NR')))

    if (canonicalTranscriptsStartByNR.length === 1) {
        return extended ? canonicalTranscriptsStartByNR[0] : canonicalTranscriptsStartByNR[0].hgvsc;
    }

    console.log('debug: did not find qualifying canonical transcript title');

    return null;

    let transcript = null;
    for (let item of transcripts) {
        // Only if nucleotide transcripts exist
        if (item.hgvsc && item.source === 'RefSeq' && item.canonical && item.canonical === 1) {
            transcript = extended ? item : item.hgvsc;
            // only take the first matching transcript
        }
    }
    if (!transcript) {
        console.log('did not find canonical transcirpt in ensembl')
    }
    return transcript;
}

/**
 * @param {Object} props The argument object of this method
 * @param {Array<object>} props.transcript_consequences The transcripts in Ensembl API response data
 * @param {'clinvar'|'car'} props.matchBySource Either 'clinvar' or 'car'
 * @param {object} props.parsedData The parsed data originated from either Clinvar or CAR.
 *
 * @returns {?string} The MANE transcript title
 */
export const getCanonicalTranscriptTitleFromEnsemblTranscripts = ({
    matchBySource,
    transcript_consequences: ensemblTranscripts,
    parsedData
}) => {
    if (matchBySource === 'car') {
        const canonicalTranscript = getCanonicalTranscript(ensemblTranscripts);
        if (canonicalTranscript && canonicalTranscript.length && parsedData.tempAlleles && parsedData.tempAlleles.length) {
            for (const item of parsedData.tempAlleles) {
                /** shape of `item` example:
                 * {
                        "geneSymbol": "RAD51",
                        "hgvs": [
                            "NM_001164269.1:c.140A>T" <-
                        ],
                        "proteinEffect": {
                            "hgvs": "NP_001157741.1:p.His47Leu", <-
                            "hgvsWellDefined": "NP_001157741.1:p.His47Leu"
                        }
                    }
                */
                if (item.hgvs && item.hgvs.length) {
                    for (const transcript of item.hgvs) {
                        if (transcript === canonicalTranscript) {
                            let proteinChange;
                            const transcriptStart = transcript.split(':')[0];
                            const transcriptEnd = transcript.split(':')[1];
                            if (item.proteinEffect && item.proteinEffect.hgvs) {
                                proteinChange = item.proteinEffect.hgvs.split(':')[1];
                            }
                            return `${transcriptStart}(${item.geneSymbol}):${transcriptEnd}${proteinChange ? ` (${proteinChange})` : ''}`;
                        }
                    }
                }
            };
        }
    } else if (matchBySource === 'clinvar') {
        const canonicalTranscriptInEnsembl = getCanonicalTranscript(ensemblTranscripts, true);

        if (!canonicalTranscriptInEnsembl) {
            console.warn(`No qualified canonical transcript found for assgining a canonical transcript title`);
            return null;
        }

        if (!canonicalTranscriptInEnsembl.hgvsc) {
            console.warn(`Cannot gather sufficient information to generate canonical transcript title, even if we found a canonical transcript in ensembl`, canonicalTranscriptInEnsembl);
            return null;
        }

        const {
            hgvsc
        } = canonicalTranscriptInEnsembl;

        const [canonicalTranscriptId, ] = hgvsc.split(':');

        return getPreferredTitleFromEnsemblTranscriptsNoMatch(canonicalTranscriptId, canonicalTranscriptInEnsembl) || null;
    }

    return null;
}
<commit_msg>add debugging log<commit_after>'use strict';

import { getPreferredTitleFromEnsemblTranscriptsNoMatch } from "./parse-resources";

/**
 * Method to return the canonical transcript given the Ensembl transcripts.
 * Note that there are possibly multiple canonical transcript(s) in Ensembl
 * transcripts, this method only picks the representative one, since this method
 * is intended to be used for getting a single canonical transcript title for a variant.
 *
 * Among the canonical transcripts, whether or not one qualifies as the representative
 * canonical transcript, is determined by algorithm below:
 *
 * 1. [**General Singularity Test**] - If only one canonical transcript exists in Ensembl transcripts, return it
 * 2. [**NM Singularity Test**] - If there're multiple canonical transcripts, and only one of them whose
 *      c. nomenclature (i.e. `hgvsc`) starts with NM, return it
 * 3. [**NM Singularity Test**] - If there're multiple canonical transcripts whose `hgvsc` starts with NM,
 *      return null, which also means we don't use canonical transcripts for variant title
 * 4, [**NR Singularity Test**] - If there're no canonical transcript with `NM` prefix, look for canonical transcripts
 *      with `NR` prefix; if there's exactly one with `NR` prefix, return it
 * 5. Otherwise return null; this includes cases of multiple caonical transcripts with NR prefix
 *
 *
 * Examples:
 *
 * e.g. 1: `NR_1, XR_1, XR_2, XM_1, XM_2 ...`: in this case there is no NM but there is one NR_1 so use that
 *
 * e.g. 2: `NR_1, NR_2, XR_1, XR_2, XM_1, XM_2 ...`: in this case skip because there is no NM and there is more than one NR
 *
 * e.g. 3: `NM_1, NR_1, NR_2, XR_1, XR_2, XM_1, XM_2 ...`: in this case use NM_1
 *
 * e.g. 4: `NM_1, NM_2_ NR_1, XR_1, XR_2, XM_1, XM_2 ...`: in this case skip because there is more than one NM
 *
 * @see {@link https://github.com/ClinGen/clincoded/issues/2176|Issue 2176}
 * @see getCanonicalTranscriptTitleFromEnsemblTranscripts
 *
 * @param {array} transcripts - Ensembl transcripts
 * @param {boolean} extended - If `true`, will return an transcript object; otherwise, will just return the hgvsc string of the transcript
 *
 * @returns {(string|object|null|undefined)} Returns the hgvs string of canonical transcript, or the entire transcript object from Ensembl API, depending on argument `extended`.
 *      Will return `null` if no canonical transcript found, or found but no `hgvsc` field on it.
 */
export function getCanonicalTranscript(transcripts, extended = false) {

    // Filter all ensembl transcripts marked as canonical

    const canonicalTranscriptsFromEnsembl = transcripts.filter((transcript) => (
        transcript.hgvsc &&
        // transcript.source === 'RefSeq' &&
        transcript.canonical
    ));

    if (canonicalTranscriptsFromEnsembl.length === 0) {
        return null;
    }

    console.log('canonical transcripts', canonicalTranscriptsFromEnsembl);

    // General Singularity Test

    if (canonicalTranscriptsFromEnsembl.length === 1) {
        return extended ? canonicalTranscriptsFromEnsembl[0] : canonicalTranscriptsFromEnsembl[0].hgvsc;
    }

    // NM Singularity Test

    const canonicalTranscriptsStartByNM = canonicalTranscriptsFromEnsembl.filter(({hgvsc = ''}) => (typeof hgvsc === 'string' && hgvsc.trim().startsWith('NM')))

    if (canonicalTranscriptsStartByNM.length === 1) {
        return extended ? canonicalTranscriptsStartByNM[0] : canonicalTranscriptsStartByNM[0].hgvsc;
    }

    // NR Singularity Test

    const canonicalTranscriptsStartByNR = canonicalTranscriptsFromEnsembl.filter(({hgvsc = ''}) => (typeof hgvsc === 'string' && hgvsc.trim().startsWith('NR')))

    if (canonicalTranscriptsStartByNR.length === 1) {
        return extended ? canonicalTranscriptsStartByNR[0] : canonicalTranscriptsStartByNR[0].hgvsc;
    }

    console.log('debug: did not find qualifying canonical transcript title');

    return null;

    let transcript = null;
    for (let item of transcripts) {
        // Only if nucleotide transcripts exist
        if (item.hgvsc && item.source === 'RefSeq' && item.canonical && item.canonical === 1) {
            transcript = extended ? item : item.hgvsc;
            // only take the first matching transcript
        }
    }
    if (!transcript) {
        console.log('did not find canonical transcirpt in ensembl')
    }
    return transcript;
}

/**
 * @param {Object} props The argument object of this method
 * @param {Array<object>} props.transcript_consequences The transcripts in Ensembl API response data
 * @param {'clinvar'|'car'} props.matchBySource Either 'clinvar' or 'car'
 * @param {object} props.parsedData The parsed data originated from either Clinvar or CAR.
 *
 * @returns {?string} The MANE transcript title
 */
export const getCanonicalTranscriptTitleFromEnsemblTranscripts = ({
    matchBySource,
    transcript_consequences: ensemblTranscripts,
    parsedData
}) => {
    if (matchBySource === 'car') {
        const canonicalTranscript = getCanonicalTranscript(ensemblTranscripts);
        if (canonicalTranscript && canonicalTranscript.length && parsedData.tempAlleles && parsedData.tempAlleles.length) {
            for (const item of parsedData.tempAlleles) {
                /** shape of `item` example:
                 * {
                        "geneSymbol": "RAD51",
                        "hgvs": [
                            "NM_001164269.1:c.140A>T" <-
                        ],
                        "proteinEffect": {
                            "hgvs": "NP_001157741.1:p.His47Leu", <-
                            "hgvsWellDefined": "NP_001157741.1:p.His47Leu"
                        }
                    }
                */
                if (item.hgvs && item.hgvs.length) {
                    for (const transcript of item.hgvs) {
                        if (transcript === canonicalTranscript) {
                            let proteinChange;
                            const transcriptStart = transcript.split(':')[0];
                            const transcriptEnd = transcript.split(':')[1];
                            if (item.proteinEffect && item.proteinEffect.hgvs) {
                                proteinChange = item.proteinEffect.hgvs.split(':')[1];
                            }
                            return `${transcriptStart}(${item.geneSymbol}):${transcriptEnd}${proteinChange ? ` (${proteinChange})` : ''}`;
                        }
                    }
                }
            };
        }
    } else if (matchBySource === 'clinvar') {
        const canonicalTranscriptInEnsembl = getCanonicalTranscript(ensemblTranscripts, true);

        if (!canonicalTranscriptInEnsembl) {
            console.warn(`No qualified canonical transcript found for assgining a canonical transcript title`);
            return null;
        }

        if (!canonicalTranscriptInEnsembl.hgvsc) {
            console.warn(`Cannot gather sufficient information to generate canonical transcript title, even if we found a canonical transcript in ensembl`, canonicalTranscriptInEnsembl);
            return null;
        }

        const {
            hgvsc
        } = canonicalTranscriptInEnsembl;

        const [canonicalTranscriptId, ] = hgvsc.split(':');

        return getPreferredTitleFromEnsemblTranscriptsNoMatch(canonicalTranscriptId, canonicalTranscriptInEnsembl) || null;
    }

    return null;
}
<|endoftext|>


<commit_before>var fs = require('fs-extra')
var c = require('chalk')
var path = require('path')
var isDir = require('is-directory')
var util = require(__dirname + '/util')
var conceptsDir = process.cwd() + '/concepts'
var dialectsDir = process.cwd() + '/dialects'
var templateDir = __dirname + '/../templates'

module.exports = function (argv) {

  if (argv._.length < 2) {
    console.log(c.red('\n Invalid format\n'))
    console.log(' Use the format ' + c.bold('tani add <path> [content]'))
    console.log(' Ex: tani add n "Sky"')
    console.log(' Ex: tani add n/animals@2 "Mithun"')
    return console.log(' Ex: tani add n/animals "Elephant"\n')
  }

  var conceptPath
  var conceptIndex
  var indexSpecified = false
  var fileName
  var newConceptFilePath
  var concept = argv._[2]

  var pathArray = argv._[1].split('@')
  if (pathArray.length === 2) {
    conceptPath = pathArray[0]
    conceptIndex = pathArray[1]
    indexSpecified = true
  } else {
    conceptPath = argv._[1]
  }

  if (!(conceptPath.split('/')[0] in util.concepts)) {
    console.log(c.red('\n Invalid concept type "'+ conceptPath.split('/')[0] +'"\n'))
    return console.log(' Supported types: ' + Object.keys(util.concepts).toString().replace(/,/g, ', ') + '\n')
  }

  var targetConceptDir = conceptsDir + '/' + conceptPath
  var conceptIndexCandidate = getNextFileId(targetConceptDir)

  if (indexSpecified) {
    // concept Index can't be more than the immediate highest number
    if (conceptIndex > conceptIndexCandidate) {
      conceptIndex = conceptIndexCandidate
      indexSpecified = false
    }
  } else {
    conceptIndex = conceptIndexCandidate
  }

  fileName = conceptIndex + '.txt'
  newConceptFilePath = targetConceptDir + '/' + fileName
  // if the dir does not exist
  fs.mkdirpSync(targetConceptDir)

  // if the concept index was specified, shift the existing files names after the index
  if (indexSpecified) incrementFileNames(targetConceptDir, conceptIndex, conceptIndexCandidate)

  // create the new concept file
  fs.ensureFileSync(newConceptFilePath)
  // if the concept string was specified, write it to the file
  if (concept) fs.writeFileSync(newConceptFilePath, concept + '\n')
  // update the dialect files to reflect the new change
  if (fs.existsSync(dialectsDir)) updateDialectFiles(targetConceptDir, conceptIndex, conceptIndexCandidate)

}

// when a new concept file is added in the middle, the corresponding dialect files must also be updated
function updateDialectFiles(sourceDir, startingIndex, highestIndex) {

  fs.readdirSync(dialectsDir).forEach(function (tribe) {
    var tribeDirPath = dialectsDir + '/' + tribe
    fs.readdirSync(tribeDirPath).forEach(function (locality) {

      (function (sourceDir, startingIndex, highestIndex) {

        var sourceConceptPath = sourceDir + '/' + startingIndex + '.txt'
        var localityDirPath = dialectsDir + '/' + tribe + '/' + locality
        var dialectConceptDir = localityDirPath + sourceDir.split('concepts')[1]
        var dialectConceptPath = dialectConceptDir + '/' + startingIndex + '.txt'

        if (!fs.existsSync(dialectConceptDir)) fs.mkdirpSync(dialectConceptDir)

        if (highestIndex !== startingIndex) {
          var fileIndex = highestIndex
          // rename existing file names
          while (fileIndex > startingIndex) {
            var oldName = dialectConceptDir + '/' + (fileIndex - 1) + '.txt'
            var newName = dialectConceptDir + '/' + (fileIndex) + '.txt'
            fs.renameSync(oldName, newName)
            fileIndex--
          }
        }

        // create the new file
        var conceptString = fs.readFileSync(sourceConceptPath).toString().trim()
        var dialectTemplate = fs.readFileSync(templateDir + '/dialect.txt').toString()
        var dialectString = dialectTemplate.replace('#ENTRY#', conceptString)
        fs.writeFileSync(dialectConceptPath, dialectString)

      })(sourceDir, startingIndex, highestIndex)

    })

  })

}

// when a file edited or added in concepts, reflect the change in the dialects
function incrementFileNames(targetDir, startingIndex, currentHighestIndex) {

  if (currentHighestIndex > startingIndex) highestCounterIndex = currentHighestIndex - 1

  while (highestCounterIndex >= startingIndex) {
    //console.log(currentHighestIndex, ' to ', currentHighestIndex + 1)
    var oldName = targetDir + '/' + highestCounterIndex + '.txt'
    var newName = targetDir + '/' + (highestCounterIndex + 1) + '.txt'
    fs.renameSync(oldName, newName)
    highestCounterIndex--
  }

}

// get the autoincremented next id for a file in a dir
function getNextFileId(targetDir) {

  if (!fs.existsSync(targetDir)) return 1;

  var highestNumber = 0
  fs.readdirSync(targetDir).forEach(function (file) {
    var filePath = targetDir + '/' + file
    if (!isDir.sync(filePath)) {
      var candidate = parseInt(file)
      if (candidate > highestNumber) {
        highestNumber = candidate
      }
    }
  })

  return ++highestNumber

}
<commit_msg>create the first concept in a dir with an index<commit_after>var fs = require('fs-extra')
var c = require('chalk')
var path = require('path')
var isDir = require('is-directory')
var util = require(__dirname + '/util')
var conceptsDir = process.cwd() + '/concepts'
var dialectsDir = process.cwd() + '/dialects'
var templateDir = __dirname + '/../templates'

module.exports = function (argv) {

  if (argv._.length < 2) {
    console.log(c.red('\n Invalid format\n'))
    console.log(' Use the format ' + c.bold('tani add <path> [content]'))
    console.log(' Ex: tani add n "Sky"')
    console.log(' Ex: tani add n/animals@2 "Mithun"')
    return console.log(' Ex: tani add n/animals "Elephant"\n')
  }

  var conceptPath
  var conceptIndex
  var indexSpecified = false
  var fileName
  var newConceptFilePath
  var concept = argv._[2]

  var pathArray = argv._[1].split('@')
  if (pathArray.length === 2) {
    conceptPath = pathArray[0]
    conceptIndex = pathArray[1]
    indexSpecified = true
  } else {
    conceptPath = argv._[1]
  }

  if (!(conceptPath.split('/')[0] in util.concepts)) {
    console.log(c.red('\n Invalid concept type "'+ conceptPath.split('/')[0] +'"\n'))
    return console.log(' Supported types: ' + Object.keys(util.concepts).toString().replace(/,/g, ', ') + '\n')
  }

  var targetConceptDir = conceptsDir + '/' + conceptPath
  var conceptIndexCandidate = getNextFileId(targetConceptDir)

  if (indexSpecified) {
    // concept Index can't be more than the immediate highest number
    if (conceptIndex > conceptIndexCandidate) {
      conceptIndex = conceptIndexCandidate
      indexSpecified = false
    }
  } else {
    conceptIndex = conceptIndexCandidate
  }

  fileName = conceptIndex + '.txt'
  newConceptFilePath = targetConceptDir + '/' + fileName
  // if the dir does not exist
  fs.mkdirpSync(targetConceptDir)

  // if the concept index was specified, shift the existing files names after the index
  if (indexSpecified) incrementFileNames(targetConceptDir, conceptIndex, conceptIndexCandidate)

  // create the new concept file
  fs.ensureFileSync(newConceptFilePath)
  // if the concept string was specified, write it to the file
  if (concept) fs.writeFileSync(newConceptFilePath, concept + '\n')
  // update the dialect files to reflect the new change
  if (fs.existsSync(dialectsDir)) updateDialectFiles(targetConceptDir, conceptIndex, conceptIndexCandidate)

}

// when a new concept file is added in the middle, the corresponding dialect files must also be updated
function updateDialectFiles(sourceDir, startingIndex, highestIndex) {

  fs.readdirSync(dialectsDir).forEach(function (tribe) {
    var tribeDirPath = dialectsDir + '/' + tribe
    fs.readdirSync(tribeDirPath).forEach(function (locality) {

      (function (sourceDir, startingIndex, highestIndex) {

        var sourceConceptPath = sourceDir + '/' + startingIndex + '.txt'
        var localityDirPath = dialectsDir + '/' + tribe + '/' + locality
        var dialectConceptDir = localityDirPath + sourceDir.split('concepts')[1]
        var dialectConceptPath = dialectConceptDir + '/' + startingIndex + '.txt'

        if (!fs.existsSync(dialectConceptDir)) fs.mkdirpSync(dialectConceptDir)

        if (highestIndex !== startingIndex) {
          var fileIndex = highestIndex
          // rename existing file names
          while (fileIndex > startingIndex) {
            var oldName = dialectConceptDir + '/' + (fileIndex - 1) + '.txt'
            var newName = dialectConceptDir + '/' + (fileIndex) + '.txt'
            fs.renameSync(oldName, newName)
            fileIndex--
          }
        }

        // create the new file
        var conceptString = fs.readFileSync(sourceConceptPath).toString().trim()
        var dialectTemplate = fs.readFileSync(templateDir + '/dialect.txt').toString()
        var dialectString = dialectTemplate.replace('#ENTRY#', conceptString)
        fs.writeFileSync(dialectConceptPath, dialectString)

      })(sourceDir, startingIndex, highestIndex)

    })

  })

}

// when a file edited or added in concepts, reflect the change in the dialects
function incrementFileNames(targetDir, startingIndex, currentHighestIndex) {

  // increment not required
  if (+startingIndex === currentHighestIndex) return

  var highestCounterIndex = currentHighestIndex - 1
  while (highestCounterIndex >= startingIndex) {
    //console.log(currentHighestIndex, ' to ', currentHighestIndex + 1)
    var oldName = targetDir + '/' + highestCounterIndex + '.txt'
    var newName = targetDir + '/' + (highestCounterIndex + 1) + '.txt'
    fs.renameSync(oldName, newName)
    highestCounterIndex--
  }

}

// get the autoincremented next id for a file in a dir
function getNextFileId(targetDir) {

  if (!fs.existsSync(targetDir)) return 1;

  var highestNumber = 0
  fs.readdirSync(targetDir).forEach(function (file) {
    var filePath = targetDir + '/' + file
    if (!isDir.sync(filePath)) {
      var candidate = parseInt(file)
      if (candidate > highestNumber) {
        highestNumber = candidate
      }
    }
  })

  return ++highestNumber

}
<|endoftext|>


<commit_before>var devices = require('ev3-js-devices')
var Device = require('ev3-js-device')
var fs = require('fs')

var sensors = getSensors()

module.exports = writeAPI

function writeAPI (socket) {
  var interval
  var reading = false

  return {
    'sensor_mode': setSensorMode,
    'motor_write': motorWrite,
    'start_sensor_read': startSensorRead,
    'stop_sensor_read': stopSensorRead
  }

  function startSensorRead () {
    if (!reading) {
      reading = true
      interval = setInterval(sensorRead, 150)
      return JSON.stringify({
        type: 'started_read',
        value: 'ok'
      })
    }
    return JSON.stringify({
      type: 'error',
      value: 'Another computer is already connected to this robot'
    })
  }

  function stopSensorRead () {
    clearInterval(interval)
  }

  function sensorRead () {
    var sensorData = {}
    for (var port in sensors) {
      var data = sensors[port].device.read('value0')
      sensorData[port] = {
        type: sensors[port].type,
        value: data
      }
    }
    try {
      socket.send(JSON.stringify({
        type: 'sensor_read',
        data: sensorData
      }))
    } catch (e) {
      console.warn('socket closed')
    }
  }
}

function setSensorMode (data) {
  var port = data.port
  var value = data.value
  var device = new Device(devices(port))
  device.write('mode', value)
}

function motorWrite (data) {
  var port = data.port
  var value = data.value
  var device = new Device(devices(port))
  device.write('command', value)
}

function getSensors () {
  return [1, 2, 3, 4].reduce(function (obj, port) {
    try {
      var path = devices(port)
      obj[port] = {
        type: fs.readFileSync(path + '/driver_name', 'utf-8').trim(),
        device: new Device(path)
      }
    } catch (e) {}
    return obj
  }, {})
}
<commit_msg>new api<commit_after>var devices = require('ev3-js-devices')
var Device = require('ev3-js-device')
var fs = require('fs')

var sensors = getSensors()
var reading = false

module.exports = writeAPI

function writeAPI (socket) {
  var interval

  return {
    'sensor_mode': setSensorMode,
    'motor_write': motorWrite,
    'start_sensor_read': startSensorRead,
    'stop_sensor_read': stopSensorRead
  }

  function startSensorRead () {
    if (!reading) {
      reading = true
      interval = setInterval(sensorRead, 150)
      return JSON.stringify({
        type: 'started_read',
        value: 'ok'
      })
    }
    return JSON.stringify({
      type: 'error',
      value: 'Another computer is already connected to this robot'
    })
  }

  function stopSensorRead () {
    reading = false
    clearInterval(interval)
  }

  function sensorRead () {
    var sensorData = {}
    for (var port in sensors) {
      var data = sensors[port].device.read('value0')
      sensorData[port] = {
        type: sensors[port].type,
        value: data
      }
    }
    try {
      socket.send(JSON.stringify({
        type: 'sensor_read',
        data: sensorData
      }))
    } catch (e) {
      console.warn('socket closed')
    }
  }
}

function setSensorMode (data) {
  var port = data.port
  var value = data.value
  var device = new Device(devices(port))
  device.write('mode', value)
}

function motorWrite (data) {
  var port = data.port
  var value = data.value
  var device = new Device(devices(port))
  device.write('command', value)
}

function getSensors () {
  return [1, 2, 3, 4].reduce(function (obj, port) {
    try {
      var path = devices(port)
      obj[port] = {
        type: fs.readFileSync(path + '/driver_name', 'utf-8').trim(),
        device: new Device(path)
      }
    } catch (e) {}
    return obj
  }, {})
}
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { withProps } from 'recompose';
import BandwidthThemeProvider from '../BandwidthThemeProvider';
import DefaultStyleRoot from './styles/StyleRoot';
import withDragDropContext from './withDragDropContext';
import DefaultDragLayer from '../DragLayer';
import './styles/global'; // injects a global stylesheet

const Provider = ({ StyleRoot, ThemeProvider, DragLayer, children }) => (
  <ThemeProvider>
    <StyleRoot>
      {children}
      <DragLayer />
    </StyleRoot>
  </ThemeProvider>
);

Provider.propTypes = {
  StyleRoot: PropTypes.func,
  ThemeProvider: PropTypes.func,
  DragLayer: PropTypes.func,
  children: PropTypes.node.isRequired,
};

Provider.defaultProps = {
  StyleRoot: DefaultStyleRoot,
  ThemeProvider: BandwidthThemeProvider,
  DragLayer: DefaultDragLayer,
};

export default withDragDropContext(Provider);
<commit_msg>Update drag context usage to avoid error about refs<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { withProps } from 'recompose';
import BandwidthThemeProvider from '../BandwidthThemeProvider';
import DefaultStyleRoot from './styles/StyleRoot';
import withDragDropContext from './withDragDropContext';
import DefaultDragLayer from '../DragLayer';
import './styles/global'; // injects a global stylesheet

class Provider extends React.PureComponent {
  static propTypes = {
    StyleRoot: PropTypes.func,
    ThemeProvider: PropTypes.func,
    DragLayer: PropTypes.func,
    children: PropTypes.node.isRequired,
  };

  static defaultProps = {
    StyleRoot: DefaultStyleRoot,
    ThemeProvider: BandwidthThemeProvider,
    DragLayer: DefaultDragLayer,
  };

  render() {
    const { StyleRoot, ThemeProvider, DragLayer, children } = this.props;
    return (
      <ThemeProvider>
        <StyleRoot>
          {children}
          <DragLayer />
        </StyleRoot>
      </ThemeProvider>
    );
  }
}

export default withDragDropContext(Provider);
<|endoftext|>


<commit_before>var express = require('express');
var bodyParser = require('body-parser');
var cors = require('cors');
var _ = require('underscore');
var fh = require('fh-mbaas-api');

var api = new express.Router();
api.use(cors());
api.use(bodyParser());

// Clever bit of hackery to reduce repetition - takes a response object,
// gives us a callback function to pass to fh.service and deal with errors or successes
function doServiceResponse(res, filter){
  return function(err, serviceResponseBody){
    if ( err ) {
      console.log('Error reaching service: ');
      console.log(err);
      return res.status(500).json(err);
    }
    if (_.isFunction(filter)){
      serviceResponseBody = filter(serviceResponseBody);
    }
    return res.json(serviceResponseBody);
  };
}

api.get('/accounts', function(req, res) {
  // Login to SF
  return fh.service({
    "guid": "5ls2muzfhb4mqkwqt4gkwik2",
    "path": "/cloud/login",
    "method": "POST",
    "params": {}
  }, function(err, authInfo, response) {
    if ( err ) {
      var status = response && response.statusCode || 500;
      return res.status(status).json(err);
    }
    // Now we've established a SF session, retrieve a list of accounts
    return fh.service({
      "guid": process.env.SALESFORCE_SERVICE,
      "path": "/cloud/listAccounts",
      "method": "POST",
      "params": authInfo
    }, doServiceResponse(res, function(unfiltered){
      return unfiltered.records;
    }));
  });
});

// Convert a sharepoint field ID into
function getInternalSharepointName(fields, name){
  var field = _.findWhere(fields, { Title : name });
  if (!field){
    return name;
  }
  return field.InternalName;
}

api.get('/products', function(req, res){
  var productListId = process.env.PRODUCTS_LIST_ID;
  fh.service({
    "guid": process.env.SHAREPOINT_SERVICE,
    "path": "/lists/" + productListId,
    "method": "GET",
    "params": {}
  }, doServiceResponse(res, function filter(unfiltered){
    // Filter the response from the products service to only contain stuff we're interested in
    var fields = unfiltered.Fields,
    products = unfiltered.Items,
    nameField = getInternalSharepointName(fields, 'name'),
    priceField = getInternalSharepointName(fields, 'price'),
    colorField = getInternalSharepointName(fields, 'color'),
    materialField = getInternalSharepointName(fields, 'material'),
    descriptionField = getInternalSharepointName(fields, 'description');

    // return something sensible as a product, rather than god-awful sharepoint response
    var filtered = _.map(products, function(product){
      return {
        name : product[nameField],
        price : product[priceField],
        color : product[colorField],
        material : product[materialField],
        description: product[descriptionField]
      }
    });
    return filtered;
  }));
});

// Retrieve orders from our DB microservice
api.get('/orders', function(req, res) {
  fh.service({
    "guid": process.env.ORDERS_SERVICE,
    "path": "/orders",
    "method": "GET",
    "params": {}
  }, doServiceResponse(res, function(unfiltered){
    return unfiltered.list;
  }));
});

// Create an order in our DB microservice
api.post('/orders', function(req, res) {
  return fh.service({
    "guid": process.env.ORDERS_SERVICE,
    "path": "/orders",
    "method": "GET",
    "params": {}
  }, doServiceResponse(res));
});

module.exports = api;
<commit_msg>Also return product id<commit_after>var express = require('express');
var bodyParser = require('body-parser');
var cors = require('cors');
var _ = require('underscore');
var fh = require('fh-mbaas-api');

var api = new express.Router();
api.use(cors());
api.use(bodyParser());

// Clever bit of hackery to reduce repetition - takes a response object,
// gives us a callback function to pass to fh.service and deal with errors or successes
function doServiceResponse(res, filter){
  return function(err, serviceResponseBody){
    if ( err ) {
      console.log('Error reaching service: ');
      console.log(err);
      return res.status(500).json(err);
    }
    if (_.isFunction(filter)){
      serviceResponseBody = filter(serviceResponseBody);
    }
    return res.json(serviceResponseBody);
  };
}

api.get('/accounts', function(req, res) {
  // Login to SF
  return fh.service({
    "guid": "5ls2muzfhb4mqkwqt4gkwik2",
    "path": "/cloud/login",
    "method": "POST",
    "params": {}
  }, function(err, authInfo, response) {
    if ( err ) {
      var status = response && response.statusCode || 500;
      return res.status(status).json(err);
    }
    // Now we've established a SF session, retrieve a list of accounts
    return fh.service({
      "guid": process.env.SALESFORCE_SERVICE,
      "path": "/cloud/listAccounts",
      "method": "POST",
      "params": authInfo
    }, doServiceResponse(res, function(unfiltered){
      return unfiltered.records;
    }));
  });
});

// Convert a sharepoint field ID into
function getInternalSharepointName(fields, name){
  var field = _.findWhere(fields, { Title : name });
  if (!field){
    return name;
  }
  return field.InternalName;
}

api.get('/products', function(req, res){
  var productListId = process.env.PRODUCTS_LIST_ID;
  fh.service({
    "guid": process.env.SHAREPOINT_SERVICE,
    "path": "/lists/" + productListId,
    "method": "GET",
    "params": {}
  }, doServiceResponse(res, function filter(unfiltered){
    // Filter the response from the products service to only contain stuff we're interested in
    var fields = unfiltered.Fields,
    products = unfiltered.Items,
    nameField = getInternalSharepointName(fields, 'name'),
    priceField = getInternalSharepointName(fields, 'price'),
    colorField = getInternalSharepointName(fields, 'color'),
    materialField = getInternalSharepointName(fields, 'material'),
    descriptionField = getInternalSharepointName(fields, 'description');

    // return something sensible as a product, rather than god-awful sharepoint response
    var filtered = _.map(products, function(product){
      return {
        name : product[nameField],
        price : product[priceField],
        color : product[colorField],
        material : product[materialField],
        description: product[descriptionField],
        id : product.Id
      }
    });
    return filtered;
  }));
});

// Retrieve orders from our DB microservice
api.get('/orders', function(req, res) {
  fh.service({
    "guid": process.env.ORDERS_SERVICE,
    "path": "/orders",
    "method": "GET",
    "params": {}
  }, doServiceResponse(res, function(unfiltered){
    return unfiltered.list;
  }));
});

// Create an order in our DB microservice
api.post('/orders', function(req, res) {
  return fh.service({
    "guid": process.env.ORDERS_SERVICE,
    "path": "/orders",
    "method": "GET",
    "params": {}
  }, doServiceResponse(res));
});

module.exports = api;
<|endoftext|>


<commit_before>require('../../stylus/components/_navigation-drawer.styl')

import Applicationable from '../../mixins/applicationable'
import Overlayable from '../../mixins/overlayable'
import Themeable from '../../mixins/themeable'

import ClickOutside from '../../directives/click-outside'
import Resize from '../../directives/resize'
import Touch from '../../directives/touch'

export default {
  name: 'v-navigation-drawer',

  mixins: [Applicationable, Overlayable, Themeable],

  directives: {
    ClickOutside,
    Resize,
    Touch
  },

  data () {
    return {
      isActive: this.value,
      isBooted: false,
      isMobile: false,
      touchArea: {
        left: 0,
        right: 0
      }
    }
  },

  props: {
    absolute: Boolean,
    clipped: Boolean,
    disableRouteWatcher: Boolean,
    enableResizeWatcher: Boolean,
    height: String,
    floating: Boolean,
    miniVariant: Boolean,
    miniVariantWidth: {
      type: [Number, String],
      default: 80
    },
    mobileBreakPoint: {
      type: Number,
      default: 1264
    },
    permanent: Boolean,
    persistent: Boolean,
    right: Boolean,
    temporary: Boolean,
    touchless: Boolean,
    width: {
      type: [Number, String],
      default: 300
    },
    value: { required: false }
  },

  computed: {
    calculatedHeight () {
      return this.height || '100%'
    },
    calculatedWidth () {
      return this.miniVariant
        ? this.miniVariantWidth
        : this.width
    },
    classes () {
      return {
        'navigation-drawer': true,
        'navigation-drawer--absolute': this.absolute,
        'navigation-drawer--clipped': this.clipped,
        'navigation-drawer--close': !this.isActive,
        'navigation-drawer--floating': this.floating,
        'navigation-drawer--is-booted': this.isBooted,
        'navigation-drawer--is-mobile': this.isMobile,
        'navigation-drawer--mini-variant': this.miniVariant,
        'navigation-drawer--open': this.isActive,
        'navigation-drawer--permanent': this.permanent,
        'navigation-drawer--persistent': this.persistent,
        'navigation-drawer--right': this.right,
        'navigation-drawer--temporary': this.temporary,
        'theme--dark': this.dark,
        'theme--light': this.light
      }
    },
    marginTop () {
      if (!this.app) return 0

      return this.clipped
        ? this.$vuetify.application.top
        : 0
    },
    maxHeight () {
      if (!this.app) return '100%'

      return this.clipped
        ? this.$vuetify.application.top + this.$vuetify.application.bottom
        : this.$vuetify.application.bottom
    },
    showOverlay () {
      return !this.permanent &&
        this.isActive &&
        (this.temporary || this.isMobile)
    },
    styles () {
      return {
        height: this.calculatedHeight,
        marginTop: `${this.marginTop}px`,
        maxHeight: `calc(100% - ${this.maxHeight}px)`,
        width: `${this.calculatedWidth}px`
      }
    }
  },

  watch: {
    $route () {
      if (!this.disableRouteWatcher) {
        this.isActive = !this.closeConditional()
      }
    },
    isActive (val) {
      this.$emit('input', val)
      this.showOverlay &&
        val &&
        this.genOverlay() ||
        this.removeOverlay()
      this.$el.scrollTop = 0
    },
    isMobile (val) {
      !val && this.removeOverlay()
    },
    permanent (val) {
      this.$emit('input', val)
    },
    value (val) {
      if (this.permanent) return
      if (val !== this.isActive) this.isActive = val
    }
  },

  mounted () {
    this.$vuetify.load(this.init)
  },

  methods: {
    init () {
      if (this.value != null) this.isActive = this.value
      else if (this.permanent) this.isActive = true
      else if (this.isMobile) this.isActive = false
      else if (!this.value &&
        (this.persistent || this.temporary)
      ) this.isActive = false
      else this.isActive = true

      setTimeout(() => (this.isBooted = true), 0)
    },
    calculateTouchArea () {
      if (!this.$el.parentNode) return
      const parentRect = this.$el.parentNode.getBoundingClientRect()

      this.touchArea = {
        left: parentRect.left + 50,
        right: parentRect.right - 50
      }
    },
    checkIfMobile () {
      this.isMobile = window.innerWidth < parseInt(this.mobileBreakPoint)
    },
    closeConditional () {
      return !this.permanent && (this.temporary || this.isMobile)
    },
    genDirectives () {
      const directives = [
        {
          name: 'click-outside',
          value: this.closeConditional
        },
        {
          name: 'resize',
          value: this.onResize
        }
      ]

      !this.touchless && directives.push({
        name: 'touch',
        value: {
          parent: true,
          left: this.swipeLeft,
          right: this.swipeRight
        }
      })

      return directives
    },
    onResize () {
      if (!this.enableResizeWatcher ||
        this.permanent ||
        this.temporary
      ) return

      this.checkIfMobile()

      this.isActive = !this.isMobile
    },
    swipeRight (e) {
      if (this.isActive && !this.right) return
      this.calculateTouchArea()

      if (Math.abs(e.touchendX - e.touchstartX) < 100) return
      else if (!this.right &&
        e.touchstartX <= this.touchArea.left
      ) this.isActive = true
      else if (this.right && this.isActive) this.isActive = false
    },
    swipeLeft (e) {
      if (this.isActive && this.right) return
      this.calculateTouchArea()

      if (Math.abs(e.touchendX - e.touchstartX) < 100) return
      else if (this.right &&
        e.touchstartX >= this.touchArea.right
      ) this.isActive = true
      else if (!this.right && this.isActive) this.isActive = false
    },
    updateApplication () {
      if (!this.app) return

      const width = !this.isActive ||
        this.$vuetify.breakpoint.width <= this.mobileBreakPoint
          ? 0
          : this.calculatedWidth

      if (this.right) {
        this.$vuetify.application.right = width
      } else {
        this.$vuetify.application.left = width
      }
    }
  },

  render (h) {
    this.updateApplication()

    const data = {
      'class': this.classes,
      style: this.styles,
      directives: this.genDirectives(),
      on: {
        click: () => this.$emit('update:miniVariant', false)
      }
    }

    return h('aside', data, [
      this.$slots.default,
      h('div', { 'class': 'navigation-drawer__border' })
    ])
  }
}
<commit_msg>fixed lint<commit_after>require('../../stylus/components/_navigation-drawer.styl')

import Applicationable from '../../mixins/applicationable'
import Overlayable from '../../mixins/overlayable'
import Themeable from '../../mixins/themeable'

import ClickOutside from '../../directives/click-outside'
import Resize from '../../directives/resize'
import Touch from '../../directives/touch'

export default {
  name: 'v-navigation-drawer',

  mixins: [Applicationable, Overlayable, Themeable],

  directives: {
    ClickOutside,
    Resize,
    Touch
  },

  data () {
    return {
      isActive: this.value,
      isBooted: false,
      isMobile: false,
      touchArea: {
        left: 0,
        right: 0
      }
    }
  },

  props: {
    absolute: Boolean,
    clipped: Boolean,
    disableRouteWatcher: Boolean,
    enableResizeWatcher: Boolean,
    height: String,
    floating: Boolean,
    miniVariant: Boolean,
    miniVariantWidth: {
      type: [Number, String],
      default: 80
    },
    mobileBreakPoint: {
      type: Number,
      default: 1264
    },
    permanent: Boolean,
    persistent: Boolean,
    right: Boolean,
    temporary: Boolean,
    touchless: Boolean,
    width: {
      type: [Number, String],
      default: 300
    },
    value: { required: false }
  },

  computed: {
    calculatedHeight () {
      return this.height || '100%'
    },
    calculatedWidth () {
      return this.miniVariant
        ? this.miniVariantWidth
        : this.width
    },
    classes () {
      return {
        'navigation-drawer': true,
        'navigation-drawer--absolute': this.absolute,
        'navigation-drawer--clipped': this.clipped,
        'navigation-drawer--close': !this.isActive,
        'navigation-drawer--floating': this.floating,
        'navigation-drawer--is-booted': this.isBooted,
        'navigation-drawer--is-mobile': this.isMobile,
        'navigation-drawer--mini-variant': this.miniVariant,
        'navigation-drawer--open': this.isActive,
        'navigation-drawer--permanent': this.permanent,
        'navigation-drawer--persistent': this.persistent,
        'navigation-drawer--right': this.right,
        'navigation-drawer--temporary': this.temporary,
        'theme--dark': this.dark,
        'theme--light': this.light
      }
    },
    marginTop () {
      if (!this.app) return 0

      return this.clipped
        ? this.$vuetify.application.top
        : 0
    },
    maxHeight () {
      if (!this.app) return '100%'

      return this.clipped
        ? this.$vuetify.application.top + this.$vuetify.application.bottom
        : this.$vuetify.application.bottom
    },
    showOverlay () {
      return !this.permanent &&
        this.isActive &&
        (this.temporary || this.isMobile)
    },
    styles () {
      return {
        height: this.calculatedHeight,
        marginTop: `${this.marginTop}px`,
        maxHeight: `calc(100% - ${this.maxHeight}px)`,
        width: `${this.calculatedWidth}px`
      }
    }
  },

  watch: {
    $route () {
      if (!this.disableRouteWatcher) {
        this.isActive = !this.closeConditional()
      }
    },
    isActive (val) {
      this.$emit('input', val)
      this.showOverlay &&
        val &&
        this.genOverlay() ||
        this.removeOverlay()
      this.$el.scrollTop = 0
    },
    isMobile (val) {
      !val && this.removeOverlay()
    },
    permanent (val) {
      this.$emit('input', val)
    },
    value (val) {
      if (this.permanent) return
      if (val !== this.isActive) this.isActive = val
    }
  },

  mounted () {
    this.$vuetify.load(this.init)
  },

  methods: {
    init () {
      if (this.value != null) this.isActive = this.value
      else if (this.permanent) this.isActive = true
      else if (this.isMobile) this.isActive = false
      else if (!this.value &&
        (this.persistent || this.temporary)
      ) this.isActive = false
      else this.isActive = true

      setTimeout(() => (this.isBooted = true), 0)
    },
    calculateTouchArea () {
      if (!this.$el.parentNode) return
      const parentRect = this.$el.parentNode.getBoundingClientRect()

      this.touchArea = {
        left: parentRect.left + 50,
        right: parentRect.right - 50
      }
    },
    checkIfMobile () {
      this.isMobile = window.innerWidth < parseInt(this.mobileBreakPoint)
    },
    closeConditional () {
      return !this.permanent && (this.temporary || this.isMobile)
    },
    genDirectives () {
      const directives = [
        {
          name: 'click-outside',
          value: this.closeConditional
        },
        {
          name: 'resize',
          value: this.onResize
        }
      ]

      !this.touchless && directives.push({
        name: 'touch',
        value: {
          parent: true,
          left: this.swipeLeft,
          right: this.swipeRight
        }
      })

      return directives
    },
    onResize () {
      if (!this.enableResizeWatcher ||
        this.permanent ||
        this.temporary
      ) return

      this.checkIfMobile()

      this.isActive = !this.isMobile
    },
    swipeRight (e) {
      if (this.isActive && !this.right) return
      this.calculateTouchArea()

      if (Math.abs(e.touchendX - e.touchstartX) < 100) return
      else if (!this.right &&
        e.touchstartX <= this.touchArea.left
      ) this.isActive = true
      else if (this.right && this.isActive) this.isActive = false
    },
    swipeLeft (e) {
      if (this.isActive && this.right) return
      this.calculateTouchArea()

      if (Math.abs(e.touchendX - e.touchstartX) < 100) return
      else if (this.right &&
        e.touchstartX >= this.touchArea.right
      ) this.isActive = true
      else if (!this.right && this.isActive) this.isActive = false
    },
    updateApplication () {
      if (!this.app) return

      const width = !this.isActive ||
        this.$vuetify.breakpoint.width <= this.mobileBreakPoint
        ? 0
        : this.calculatedWidth

      if (this.right) {
        this.$vuetify.application.right = width
      } else {
        this.$vuetify.application.left = width
      }
    }
  },

  render (h) {
    this.updateApplication()

    const data = {
      'class': this.classes,
      style: this.styles,
      directives: this.genDirectives(),
      on: {
        click: () => this.$emit('update:miniVariant', false)
      }
    }

    return h('aside', data, [
      this.$slots.default,
      h('div', { 'class': 'navigation-drawer__border' })
    ])
  }
}
<|endoftext|>


<commit_before>/*jslint devel: true, nomen: true, plusplus: true, regexp: true, indent: 2, maxlen: 100 */

"use strict";

var logger = require('./logger');
var async = require('async');
var url = require('url');
var http = require('http');
var https = require('https');
var stringify = require('json-stringify-safe');

exports.VERSION = '1';
exports.endpoint = 'https://api.rollbar.com/api/' + exports.VERSION + '/';
exports.accessToken = null;

var SETTINGS = {
  accessToken: null,
  protocol: 'https',
  endpoint: exports.endpoint,
  proxy: null,
  retryInterval: null
};

var retryQueue = [];
var retryHandle = null;

/*
 * Internal
 */

function transportOpts(path, method) {
  var port;
  port = SETTINGS.port ||
      (SETTINGS.protocol === 'http' ? 80 : (SETTINGS.protocol === 'https' ? 443 : undefined));

  return {
    host: SETTINGS.endpointOpts.host,
    port: port,
    path: SETTINGS.endpointOpts.path + path,
    method: method
  };
}

function parseApiResponse(respData, callback) {
  try {
    respData = JSON.parse(respData);
  } catch (e) {
    logger.error('Could not parse api response, err: ' + e);
    return callback(e);
  }

  if (respData.err) {
    logger.error('Received error: ' + respData.message);
    return callback(new Error('Api error: ' + (respData.message || 'Unknown error')));
  }

  if (respData.result && respData.result.uuid) {
    logger.log([
      'Successful api response.',
      ' Link: https://rollbar.com/occurrence/uuid/?uuid=' + respData.result.uuid
    ].join(''));

  } else {
    logger.log('Successful api response');
  }

  callback(null, respData.result);
}

function pushOntoRetryQueue() {
  retryQueue.push(arguments);

  if (!retryHandle) {
    retryHandle = setInterval(function() {
      while (retryQueue.length) {
        makeApiRequest.apply(null, retryQueue.splice(0, 1)[0]);
      }
    }, SETTINGS.retryInterval);
  }
}

function makeApiRequest(transport, opts, bodyObj, callback) {
  var writeData, req;

  if (!bodyObj) {
    return callback(new Error('Cannot send empty request'));
  }

  try {
    try {
      writeData = JSON.stringify(bodyObj);
    } catch (e) {
      logger.error('Could not serialize to JSON - falling back to safe-stringify');
      writeData = stringify(bodyObj);
    }
  } catch (e) {
    logger.error('Could not safe-stringify data. Giving up');
    return callback(e);
  }

  opts.headers = opts.headers || {};

  opts.headers['Content-Type'] = 'application/json';
  opts.headers['Content-Length'] = Buffer.byteLength(writeData, 'utf8');
  opts.headers['X-Rollbar-Access-Token'] = exports.accessToken;

  if (SETTINGS.proxy) {
    opts.path = SETTINGS.protocol + '://' + opts.host + opts.path;
    opts.host = SETTINGS.proxy.host;
    opts.port = SETTINGS.proxy.port;
    transport = http;
  }

  req = transport.request(opts, function (resp) {
    var respData = [];

    resp.setEncoding('utf8');
    resp.on('data', function (chunk) {
      respData.push(chunk);
    });

    resp.on('end', function () {
      respData = respData.join('');
      parseApiResponse(respData, callback);
    });
  });

  var originalArguments = arguments;
  req.on('error', function (err) {
    // If the request to Rollbar failed due to a connection error, lets queue
    // up the requests and try again periodically in the hopes that the connection
    // will be restored.
    //
    // If no retryInterval set, interpret that as not wanting to retry send errors,
    // so just bail out silently.
    if (SETTINGS.retryInterval && err.code == 'ENOTFOUND') {
      pushOntoRetryQueue.apply(null, originalArguments);
    } else {
      logger.error('Could not make request to rollbar, ' + err);
      callback(err);
    }
  });

  if (writeData) {
    req.write(writeData);
  }
  req.end();
}

function postApi(path, payload, callback) {
  var transport, opts;

  transport = SETTINGS.transport;
  opts = transportOpts(path, 'POST');

  return makeApiRequest(transport, opts, payload, callback);
}

function buildPayload(data) {
  var payload;

  // The API does not allow a context to be an object.  We need to detect if an
  // object context is provided.  If so, serialize it.
  if (typeof data.context == 'object') {
    try {
      data.context = JSON.stringify(data.context);
    } catch (e) {
      data.context = stringify(data.context);
    }
    if (data.context.length > 255) {
      data.context = data.context.substr(0, 255);
    }
  }

  payload = {
    access_token: exports.accessToken,
    data: data
  };

  return payload;
}

/*
 * Public API
 */

exports.init = function (accessToken, options) {
  var opt, portCheck;

  options = options || {};
  exports.accessToken = accessToken;
  exports.endpoint = options.endpoint || exports.endpoint;

  for (opt in options) {
    if (options.hasOwnProperty(opt)) {
      SETTINGS[opt] = options[opt];
    }
  }

  SETTINGS.endpointOpts = url.parse(exports.endpoint);
  SETTINGS.protocol = SETTINGS.endpointOpts.protocol.split(':')[0];
  SETTINGS.transport = {http: http, https: https}[SETTINGS.protocol];
  SETTINGS.proxy = options.proxy;

  portCheck = SETTINGS.endpointOpts.host.split(':');
  if (portCheck.length > 1) {
    SETTINGS.endpointOpts.host = portCheck[0];
    SETTINGS.port = parseInt(portCheck[1], 10);
  }
};

exports.postItem = function (item, callback) {
  return postApi('item/', buildPayload(item), callback);
};
<commit_msg>use shift instead of splice<commit_after>/*jslint devel: true, nomen: true, plusplus: true, regexp: true, indent: 2, maxlen: 100 */

"use strict";

var logger = require('./logger');
var async = require('async');
var url = require('url');
var http = require('http');
var https = require('https');
var stringify = require('json-stringify-safe');

exports.VERSION = '1';
exports.endpoint = 'https://api.rollbar.com/api/' + exports.VERSION + '/';
exports.accessToken = null;

var SETTINGS = {
  accessToken: null,
  protocol: 'https',
  endpoint: exports.endpoint,
  proxy: null,
  retryInterval: null
};

var retryQueue = [];
var retryHandle = null;

/*
 * Internal
 */

function transportOpts(path, method) {
  var port;
  port = SETTINGS.port ||
      (SETTINGS.protocol === 'http' ? 80 : (SETTINGS.protocol === 'https' ? 443 : undefined));

  return {
    host: SETTINGS.endpointOpts.host,
    port: port,
    path: SETTINGS.endpointOpts.path + path,
    method: method
  };
}

function parseApiResponse(respData, callback) {
  try {
    respData = JSON.parse(respData);
  } catch (e) {
    logger.error('Could not parse api response, err: ' + e);
    return callback(e);
  }

  if (respData.err) {
    logger.error('Received error: ' + respData.message);
    return callback(new Error('Api error: ' + (respData.message || 'Unknown error')));
  }

  if (respData.result && respData.result.uuid) {
    logger.log([
      'Successful api response.',
      ' Link: https://rollbar.com/occurrence/uuid/?uuid=' + respData.result.uuid
    ].join(''));

  } else {
    logger.log('Successful api response');
  }

  callback(null, respData.result);
}

function pushOntoRetryQueue() {
  retryQueue.push(arguments);

  if (!retryHandle) {
    retryHandle = setInterval(function() {
      while (retryQueue.length) {
        makeApiRequest.apply(null, retryQueue.shift());
      }
    }, SETTINGS.retryInterval);
  }
}

function makeApiRequest(transport, opts, bodyObj, callback) {
  var writeData, req;

  if (!bodyObj) {
    return callback(new Error('Cannot send empty request'));
  }

  try {
    try {
      writeData = JSON.stringify(bodyObj);
    } catch (e) {
      logger.error('Could not serialize to JSON - falling back to safe-stringify');
      writeData = stringify(bodyObj);
    }
  } catch (e) {
    logger.error('Could not safe-stringify data. Giving up');
    return callback(e);
  }

  opts.headers = opts.headers || {};

  opts.headers['Content-Type'] = 'application/json';
  opts.headers['Content-Length'] = Buffer.byteLength(writeData, 'utf8');
  opts.headers['X-Rollbar-Access-Token'] = exports.accessToken;

  if (SETTINGS.proxy) {
    opts.path = SETTINGS.protocol + '://' + opts.host + opts.path;
    opts.host = SETTINGS.proxy.host;
    opts.port = SETTINGS.proxy.port;
    transport = http;
  }

  req = transport.request(opts, function (resp) {
    var respData = [];

    resp.setEncoding('utf8');
    resp.on('data', function (chunk) {
      respData.push(chunk);
    });

    resp.on('end', function () {
      respData = respData.join('');
      parseApiResponse(respData, callback);
    });
  });

  var originalArguments = arguments;
  req.on('error', function (err) {
    // If the request to Rollbar failed due to a connection error, lets queue
    // up the requests and try again periodically in the hopes that the connection
    // will be restored.
    //
    // If no retryInterval set, interpret that as not wanting to retry send errors,
    // so just bail out silently.
    if (SETTINGS.retryInterval && err.code == 'ENOTFOUND') {
      pushOntoRetryQueue.apply(null, originalArguments);
    } else {
      logger.error('Could not make request to rollbar, ' + err);
      callback(err);
    }
  });

  if (writeData) {
    req.write(writeData);
  }
  req.end();
}

function postApi(path, payload, callback) {
  var transport, opts;

  transport = SETTINGS.transport;
  opts = transportOpts(path, 'POST');

  return makeApiRequest(transport, opts, payload, callback);
}

function buildPayload(data) {
  var payload;

  // The API does not allow a context to be an object.  We need to detect if an
  // object context is provided.  If so, serialize it.
  if (typeof data.context == 'object') {
    try {
      data.context = JSON.stringify(data.context);
    } catch (e) {
      data.context = stringify(data.context);
    }
    if (data.context.length > 255) {
      data.context = data.context.substr(0, 255);
    }
  }

  payload = {
    access_token: exports.accessToken,
    data: data
  };

  return payload;
}

/*
 * Public API
 */

exports.init = function (accessToken, options) {
  var opt, portCheck;

  options = options || {};
  exports.accessToken = accessToken;
  exports.endpoint = options.endpoint || exports.endpoint;

  for (opt in options) {
    if (options.hasOwnProperty(opt)) {
      SETTINGS[opt] = options[opt];
    }
  }

  SETTINGS.endpointOpts = url.parse(exports.endpoint);
  SETTINGS.protocol = SETTINGS.endpointOpts.protocol.split(':')[0];
  SETTINGS.transport = {http: http, https: https}[SETTINGS.protocol];
  SETTINGS.proxy = options.proxy;

  portCheck = SETTINGS.endpointOpts.host.split(':');
  if (portCheck.length > 1) {
    SETTINGS.endpointOpts.host = portCheck[0];
    SETTINGS.port = parseInt(portCheck[1], 10);
  }
};

exports.postItem = function (item, callback) {
  return postApi('item/', buildPayload(item), callback);
};
<|endoftext|>


<commit_before>/*
Copyright 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import * as sdk from '../../../index';
import withValidation from '../../views/elements/Validation';
import { decodeRecoveryKey } from 'matrix-js-sdk/src/crypto/recoverykey';
import {
    SetupEncryptionStore,
    PHASE_INTRO,
    PHASE_RECOVERY_KEY,
    PHASE_BUSY,
    PHASE_DONE,
    PHASE_CONFIRM_SKIP,
    PHASE_FINISHED,
} from '../../../stores/SetupEncryptionStore';

function keyHasPassphrase(keyInfo) {
    return (
        keyInfo.passphrase &&
        keyInfo.passphrase.salt &&
        keyInfo.passphrase.iterations
    );
}

export default class SetupEncryptionBody extends React.Component {
    static propTypes = {
        onFinished: PropTypes.func.isRequired,
    };

    constructor() {
        super();
        const store = SetupEncryptionStore.sharedInstance();
        store.on("update", this._onStoreUpdate);
        store.start();
        this.state = {
            phase: store.phase,
            // this serves dual purpose as the object for the request logic and
            // the presence of it indicating that we're in 'verify mode'.
            // Because of the latter, it lives in the state.
            verificationRequest: store.verificationRequest,
            backupInfo: store.backupInfo,
            recoveryKey: '',
            // whether the recovery key is a valid recovery key
            recoveryKeyValid: null,
            // whether the recovery key is the correct key or not
            recoveryKeyCorrect: null,
        };
    }

    _onStoreUpdate = () => {
        const store = SetupEncryptionStore.sharedInstance();
        if (store.phase === PHASE_FINISHED) {
            this.props.onFinished();
            return;
        }
        this.setState({
            phase: store.phase,
            verificationRequest: store.verificationRequest,
            backupInfo: store.backupInfo,
        });
    };

    componentWillUnmount() {
        const store = SetupEncryptionStore.sharedInstance();
        store.off("update", this._onStoreUpdate);
        store.stop();
    }

    _onResetClick = () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.startKeyReset();
    }

    _onUseRecoveryKeyClick = async () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.useRecoveryKey();
    }

    _onRecoveryKeyCancelClick() {
        const store = SetupEncryptionStore.sharedInstance();
        store.cancelUseRecoveryKey();
    }

    onSkipClick = () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.skip();
    }

    onSkipConfirmClick = () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.skipConfirm();
    }

    onSkipBackClick = () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.returnAfterSkip();
    }

    onDoneClick = () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.done();
    }

    _onUsePassphraseClick = () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.usePassPhrase();
    }

    _onRecoveryKeyChange = (e) => {
        this.setState({recoveryKey: e.target.value});
    }

    _onRecoveryKeyValidate = async (fieldState) => {
        const result = await this._validateRecoveryKey(fieldState);
        this.setState({recoveryKeyValid: result.valid});
        return result;
    }

    _validateRecoveryKey = withValidation({
        rules: [
            {
                key: "required",
                test: async (state) => {
                    try {
                        const decodedKey = decodeRecoveryKey(state.value);
                        const correct = await MatrixClientPeg.get().checkSecretStorageKey(
                            decodedKey, SetupEncryptionStore.sharedInstance().keyInfo,
                        );
                        this.setState({
                            recoveryKeyValid: true,
                            recoveryKeyCorrect: correct,
                        });
                        return correct;
                    } catch (e) {
                        this.setState({
                            recoveryKeyValid: false,
                            recoveryKeyCorrect: false,
                        });
                        return false;
                    }
                },
                invalid: function() {
                    if (this.state.recoveryKeyValid) {
                        return _t("This isn't the recovery key for your account");
                    } else {
                        return _t("This isn't a valid recovery key");
                    }
                },
                valid: function() {
                    return _t("Looks good!");
                },
            },
        ],
    })

    _onRecoveryKeyFormSubmit = (e) => {
        e.preventDefault();
        if (!this.state.recoveryKeyCorrect) return;

        const store = SetupEncryptionStore.sharedInstance();
        store.setupWithRecoveryKey(decodeRecoveryKey(this.state.recoveryKey));
    }

    render() {
        const AccessibleButton = sdk.getComponent("elements.AccessibleButton");

        const {
            phase,
        } = this.state;

        if (this.state.verificationRequest) {
            const EncryptionPanel = sdk.getComponent("views.right_panel.EncryptionPanel");
            return <EncryptionPanel
                layout="dialog"
                verificationRequest={this.state.verificationRequest}
                onClose={this.props.onFinished}
                member={MatrixClientPeg.get().getUser(this.state.verificationRequest.otherUserId)}
            />;
        } else if (phase === PHASE_INTRO) {
            const store = SetupEncryptionStore.sharedInstance();
            let recoveryKeyPrompt;
            if (keyHasPassphrase(store.keyInfo)) {
                recoveryKeyPrompt = _t("Use Recovery Key or Passphrase");
            } else {
                recoveryKeyPrompt = _t("Use Recovery Key");
            }
            return (

{_t(
                        "Confirm your identity by verifying this login from one of your other sessions, " +
                        "granting it access to encrypted messages.",
                    )}

{_t(
                        "This requires the latest Riot on your other devices:",
                    )}

Riot Web

Riot Desktop

Riot iOS

Riot X for Android

{_t("or another cross-signing capable Matrix client")}

                        <AccessibleButton kind="link" onClick={this._onUseRecoveryKeyClick}>
                            {recoveryKeyPrompt}
                        </AccessibleButton>
                        <AccessibleButton kind="danger" onClick={this.onSkipClick}>
                            {_t("Skip")}
                        </AccessibleButton>

{_t(
                        "If you've forgotten your recovery key you can " +
                        "<button>set up new recovery options</button>", {}, {
                            button: sub => <AccessibleButton
                                element="span" className="mx_linkButton" onClick={this._onResetClick}
                            >
                                {sub}
                            </AccessibleButton>,
                        },
                    )}

            );
        } else if (phase === PHASE_RECOVERY_KEY) {
            const store = SetupEncryptionStore.sharedInstance();
            let keyPrompt;
            if (keyHasPassphrase(store.keyInfo)) {
                keyPrompt = _t(
                    "Enter your Recovery Key or enter a Recovery Passphrase to continue.", {},
                    {
                        a: sub => <AccessibleButton
                            element="span"
                            className="mx_linkButton"
                            onClick={this._onUsePassphraseClick}
                        >{sub}</AccessibleButton>,
                    },
                );
            } else {
                keyPrompt = _t("Enter your Recovery Key to continue.");
            }

            const Field = sdk.getComponent('elements.Field');
            return <form onSubmit={this._onRecoveryKeyFormSubmit}>

{keyPrompt}

                    <Field
                        type="text"
                        label={_t('Recovery Key')}
                        value={this.state.recoveryKey}
                        onChange={this._onRecoveryKeyChange}
                        onValidate={this._onRecoveryKeyValidate}
                    />

                    <AccessibleButton kind="secondary" onClick={this._onRecoveryKeyCancelClick}>
                        {_t("Cancel")}
                    </AccessibleButton>
                    <AccessibleButton kind="primary"
                        disabled={!this.state.recoveryKeyCorrect}
                    >
                        {_t("Continue")}
                    </AccessibleButton>

            </form>;
        } else if (phase === PHASE_DONE) {
            let message;
            if (this.state.backupInfo) {
                message =
{_t(
                    "Your new session is now verified. It has access to your " +
                    "encrypted messages, and other users will see it as trusted.",
                )}
;
            } else {
                message =
{_t(
                    "Your new session is now verified. Other users will see it as trusted.",
                )}
;
            }
            return (

                    {message}

                        <AccessibleButton
                            kind="primary"
                            onClick={this.onDoneClick}
                        >
                            {_t("Done")}
                        </AccessibleButton>

            );
        } else if (phase === PHASE_CONFIRM_SKIP) {
            return (

{_t(
                        "Without completing security on this session, it won’t have " +
                        "access to encrypted messages.",
                    )}

                        <AccessibleButton
                            className="warning"
                            kind="secondary"
                            onClick={this.onSkipConfirmClick}
                        >
                            {_t("Skip")}
                        </AccessibleButton>
                        <AccessibleButton
                            kind="danger"
                            onClick={this.onSkipBackClick}
                        >
                            {_t("Go Back")}
                        </AccessibleButton>

            );
        } else if (phase === PHASE_BUSY) {
            const Spinner = sdk.getComponent('views.elements.Spinner');
            return <Spinner />;
        } else {
            console.log(`SetupEncryptionBody: Unknown phase ${phase}`);
        }
    }
}
<commit_msg>Make the continue button actually work<commit_after>/*
Copyright 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import * as sdk from '../../../index';
import withValidation from '../../views/elements/Validation';
import { decodeRecoveryKey } from 'matrix-js-sdk/src/crypto/recoverykey';
import {
    SetupEncryptionStore,
    PHASE_INTRO,
    PHASE_RECOVERY_KEY,
    PHASE_BUSY,
    PHASE_DONE,
    PHASE_CONFIRM_SKIP,
    PHASE_FINISHED,
} from '../../../stores/SetupEncryptionStore';

function keyHasPassphrase(keyInfo) {
    return (
        keyInfo.passphrase &&
        keyInfo.passphrase.salt &&
        keyInfo.passphrase.iterations
    );
}

export default class SetupEncryptionBody extends React.Component {
    static propTypes = {
        onFinished: PropTypes.func.isRequired,
    };

    constructor() {
        super();
        const store = SetupEncryptionStore.sharedInstance();
        store.on("update", this._onStoreUpdate);
        store.start();
        this.state = {
            phase: store.phase,
            // this serves dual purpose as the object for the request logic and
            // the presence of it indicating that we're in 'verify mode'.
            // Because of the latter, it lives in the state.
            verificationRequest: store.verificationRequest,
            backupInfo: store.backupInfo,
            recoveryKey: '',
            // whether the recovery key is a valid recovery key
            recoveryKeyValid: null,
            // whether the recovery key is the correct key or not
            recoveryKeyCorrect: null,
        };
    }

    _onStoreUpdate = () => {
        const store = SetupEncryptionStore.sharedInstance();
        if (store.phase === PHASE_FINISHED) {
            this.props.onFinished();
            return;
        }
        this.setState({
            phase: store.phase,
            verificationRequest: store.verificationRequest,
            backupInfo: store.backupInfo,
        });
    };

    componentWillUnmount() {
        const store = SetupEncryptionStore.sharedInstance();
        store.off("update", this._onStoreUpdate);
        store.stop();
    }

    _onResetClick = () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.startKeyReset();
    }

    _onUseRecoveryKeyClick = async () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.useRecoveryKey();
    }

    _onRecoveryKeyCancelClick() {
        const store = SetupEncryptionStore.sharedInstance();
        store.cancelUseRecoveryKey();
    }

    onSkipClick = () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.skip();
    }

    onSkipConfirmClick = () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.skipConfirm();
    }

    onSkipBackClick = () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.returnAfterSkip();
    }

    onDoneClick = () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.done();
    }

    _onUsePassphraseClick = () => {
        const store = SetupEncryptionStore.sharedInstance();
        store.usePassPhrase();
    }

    _onRecoveryKeyChange = (e) => {
        this.setState({recoveryKey: e.target.value});
    }

    _onRecoveryKeyValidate = async (fieldState) => {
        const result = await this._validateRecoveryKey(fieldState);
        this.setState({recoveryKeyValid: result.valid});
        return result;
    }

    _validateRecoveryKey = withValidation({
        rules: [
            {
                key: "required",
                test: async (state) => {
                    try {
                        const decodedKey = decodeRecoveryKey(state.value);
                        const correct = await MatrixClientPeg.get().checkSecretStorageKey(
                            decodedKey, SetupEncryptionStore.sharedInstance().keyInfo,
                        );
                        this.setState({
                            recoveryKeyValid: true,
                            recoveryKeyCorrect: correct,
                        });
                        return correct;
                    } catch (e) {
                        this.setState({
                            recoveryKeyValid: false,
                            recoveryKeyCorrect: false,
                        });
                        return false;
                    }
                },
                invalid: function() {
                    if (this.state.recoveryKeyValid) {
                        return _t("This isn't the recovery key for your account");
                    } else {
                        return _t("This isn't a valid recovery key");
                    }
                },
                valid: function() {
                    return _t("Looks good!");
                },
            },
        ],
    })

    _onRecoveryKeyFormSubmit = (e) => {
        e.preventDefault();
        if (!this.state.recoveryKeyCorrect) return;

        const store = SetupEncryptionStore.sharedInstance();
        store.setupWithRecoveryKey(decodeRecoveryKey(this.state.recoveryKey));
    }

    render() {
        const AccessibleButton = sdk.getComponent("elements.AccessibleButton");

        const {
            phase,
        } = this.state;

        if (this.state.verificationRequest) {
            const EncryptionPanel = sdk.getComponent("views.right_panel.EncryptionPanel");
            return <EncryptionPanel
                layout="dialog"
                verificationRequest={this.state.verificationRequest}
                onClose={this.props.onFinished}
                member={MatrixClientPeg.get().getUser(this.state.verificationRequest.otherUserId)}
            />;
        } else if (phase === PHASE_INTRO) {
            const store = SetupEncryptionStore.sharedInstance();
            let recoveryKeyPrompt;
            if (keyHasPassphrase(store.keyInfo)) {
                recoveryKeyPrompt = _t("Use Recovery Key or Passphrase");
            } else {
                recoveryKeyPrompt = _t("Use Recovery Key");
            }
            return (

{_t(
                        "Confirm your identity by verifying this login from one of your other sessions, " +
                        "granting it access to encrypted messages.",
                    )}

{_t(
                        "This requires the latest Riot on your other devices:",
                    )}

Riot Web

Riot Desktop

Riot iOS

Riot X for Android

{_t("or another cross-signing capable Matrix client")}

                        <AccessibleButton kind="link" onClick={this._onUseRecoveryKeyClick}>
                            {recoveryKeyPrompt}
                        </AccessibleButton>
                        <AccessibleButton kind="danger" onClick={this.onSkipClick}>
                            {_t("Skip")}
                        </AccessibleButton>

{_t(
                        "If you've forgotten your recovery key you can " +
                        "<button>set up new recovery options</button>", {}, {
                            button: sub => <AccessibleButton
                                element="span" className="mx_linkButton" onClick={this._onResetClick}
                            >
                                {sub}
                            </AccessibleButton>,
                        },
                    )}

            );
        } else if (phase === PHASE_RECOVERY_KEY) {
            const store = SetupEncryptionStore.sharedInstance();
            let keyPrompt;
            if (keyHasPassphrase(store.keyInfo)) {
                keyPrompt = _t(
                    "Enter your Recovery Key or enter a Recovery Passphrase to continue.", {},
                    {
                        a: sub => <AccessibleButton
                            element="span"
                            className="mx_linkButton"
                            onClick={this._onUsePassphraseClick}
                        >{sub}</AccessibleButton>,
                    },
                );
            } else {
                keyPrompt = _t("Enter your Recovery Key to continue.");
            }

            const Field = sdk.getComponent('elements.Field');
            return <form onSubmit={this._onRecoveryKeyFormSubmit}>

{keyPrompt}

                    <Field
                        type="text"
                        label={_t('Recovery Key')}
                        value={this.state.recoveryKey}
                        onChange={this._onRecoveryKeyChange}
                        onValidate={this._onRecoveryKeyValidate}
                    />

                    <AccessibleButton kind="secondary" onClick={this._onRecoveryKeyCancelClick}>
                        {_t("Cancel")}
                    </AccessibleButton>
                    <AccessibleButton kind="primary"
                        disabled={!this.state.recoveryKeyCorrect}
                        onClick={this._onRecoveryKeyFormSubmit}
                    >
                        {_t("Continue")}
                    </AccessibleButton>

            </form>;
        } else if (phase === PHASE_DONE) {
            let message;
            if (this.state.backupInfo) {
                message =
{_t(
                    "Your new session is now verified. It has access to your " +
                    "encrypted messages, and other users will see it as trusted.",
                )}
;
            } else {
                message =
{_t(
                    "Your new session is now verified. Other users will see it as trusted.",
                )}
;
            }
            return (

                    {message}

                        <AccessibleButton
                            kind="primary"
                            onClick={this.onDoneClick}
                        >
                            {_t("Done")}
                        </AccessibleButton>

            );
        } else if (phase === PHASE_CONFIRM_SKIP) {
            return (

{_t(
                        "Without completing security on this session, it won’t have " +
                        "access to encrypted messages.",
                    )}

                        <AccessibleButton
                            className="warning"
                            kind="secondary"
                            onClick={this.onSkipConfirmClick}
                        >
                            {_t("Skip")}
                        </AccessibleButton>
                        <AccessibleButton
                            kind="danger"
                            onClick={this.onSkipBackClick}
                        >
                            {_t("Go Back")}
                        </AccessibleButton>

            );
        } else if (phase === PHASE_BUSY) {
            const Spinner = sdk.getComponent('views.elements.Spinner');
            return <Spinner />;
        } else {
            console.log(`SetupEncryptionBody: Unknown phase ${phase}`);
        }
    }
}
<|endoftext|>