diff --git a/app/actions/async.js b/app/actions/async.js deleted file mode 100644 index 69cc66058..000000000 --- a/app/actions/async.js +++ /dev/null @@ -1,809 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2015, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; -import semver from 'semver'; -import os from 'os'; -import { push } from 'connected-react-router'; - -import sundial from 'sundial'; - -import * as actionTypes from '../constants/actionTypes'; -import * as actionSources from '../constants/actionSources'; -import { pages, pagesMap, paths, steps, urls } from '../constants/otherConstants'; -import errorText from '../constants/errors'; -import * as metrics from '../constants/metrics'; - -import * as syncActions from './sync'; -import * as actionUtils from './utils'; -import personUtils from '../../lib/core/personUtils'; -import driverManifests from '../../lib/core/driverManifests'; -import env from '../utils/env'; - -let services = {}; -let versionInfo = {}; -let daysForCareLink = null; -let hostMap = { - 'darwin': 'mac', - 'win32' : 'win', - 'linux': 'linux', -}; -/* - * ASYNCHRONOUS ACTION CREATORS - */ - -export function doAppInit(opts, servicesToInit) { - return (dispatch, getState) => { - // when we are developing with hot reload, we get into trouble if we try to initialize the app - // when it's already been initialized, so we check the working.initializingApp flag first - if (getState().working.initializingApp === false) { - console.log('App already initialized! Skipping initialization.'); - return; - } - services = servicesToInit; - versionInfo.semver = opts.version; - versionInfo.name = opts.namedVersion; - daysForCareLink = opts.DEFAULT_CARELINK_DAYS; - const { api, carelink, device, localStore, log } = services; - - dispatch(syncActions.initRequest()); - dispatch(syncActions.hideUnavailableDevices(opts.os || hostMap[os.platform()])); - - log('Initializing local store.'); - localStore.init(localStore.getInitialState(), function(localStoreResult){ - log('Initializing device'); - device.init({ - api, - version: opts.namedVersion - }, function(deviceError, deviceResult){ - if (deviceError) { - return dispatch(syncActions.initFailure(deviceError)); - } - log('Initializing CareLink'); - carelink.init({ api }, function(carelinkError, carelinkResult){ - if (carelinkError) { - return dispatch(syncActions.initFailure(carelinkError)); - } - log('Initializing api'); - api.init(function(apiError, apiResult){ - if (apiError) { - return dispatch(syncActions.initFailure(apiError)); - } - log('Setting all api hosts', opts.environment); - api.setHosts(_.pick(opts, ['API_URL', 'UPLOAD_URL', 'BLIP_URL', 'environment'])); - dispatch(syncActions.setForgotPasswordUrl(api.makeBlipUrl(paths.FORGOT_PASSWORD))); - dispatch(syncActions.setSignUpUrl(api.makeBlipUrl(paths.SIGNUP))); - dispatch(syncActions.setNewPatientUrl(api.makeBlipUrl(paths.NEW_PATIENT))); - let session = apiResult; - if (session === undefined) { - dispatch(setPage(pages.LOGIN)); - dispatch(syncActions.initSuccess()); - return dispatch(doVersionCheck()); - } - - api.user.initializationInfo((err, results) => { - if (err) { - return dispatch(syncActions.initFailure(err)); - } - dispatch(syncActions.initSuccess()); - dispatch(doVersionCheck()); - dispatch(syncActions.setUserInfoFromToken({ - user: results[0], - profile: results[1], - memberships: results[2] - })); - const { uploadTargetUser } = getState(); - if (uploadTargetUser !== null) { - dispatch(syncActions.setBlipViewDataUrl( - api.makeBlipUrl(actionUtils.viewDataPathForUser(uploadTargetUser)) - )); - } - dispatch(retrieveTargetsFromStorage()); - }); - }); - }); - }); - }); - }; -} - -export function doLogin(creds, opts) { - return (dispatch, getState) => { - const { api } = services; - dispatch(syncActions.loginRequest()); - - api.user.loginExtended(creds, opts, (err, results) => { - if (err) { - return dispatch(syncActions.loginFailure(err.status)); - } - dispatch(syncActions.loginSuccess({ - user: results[0].user, - profile: results[1], - memberships: results[2] - })); - - // detect if a VCA here and redirect to clinic user select screen - if(personUtils.userHasRole(results[0].user, 'clinic')){ - return dispatch(setPage(pages.CLINIC_USER_SELECT, actionSources.USER, {metric: {eventName: metrics.CLINIC_SEARCH_DISPLAYED}})); - } - - // detect if a DSA here and redirect to data storage screen - const { targetUsersForUpload } = getState(); - if (_.isEmpty(targetUsersForUpload)) { - return dispatch(setPage(pages.NO_UPLOAD_TARGETS)); - } - - const { uploadTargetUser } = getState(); - if (uploadTargetUser !== null) { - dispatch(syncActions.setBlipViewDataUrl( - api.makeBlipUrl(actionUtils.viewDataPathForUser(uploadTargetUser)) - )); - } - dispatch(retrieveTargetsFromStorage()); - }); - }; -} - -export function doLogout() { - return (dispatch) => { - const { api } = services; - dispatch(syncActions.logoutRequest()); - api.user.logout((err) => { - if (err) { - dispatch(syncActions.logoutFailure()); - dispatch(setPage(pages.LOGIN, actionSources.USER)); - } - else { - dispatch(syncActions.logoutSuccess()); - dispatch(setPage(pages.LOGIN, actionSources.USER)); - } - }); - }; -} - -export function doCareLinkUpload(deviceKey, creds, utc) { - return (dispatch, getState) => { - const { api, carelink } = services; - const version = versionInfo.semver; - const { devices, targetTimezones, uploadTargetUser } = getState(); - - const targetDevice = devices[deviceKey]; - - dispatch(syncActions.fetchCareLinkRequest(uploadTargetUser, deviceKey)); - - api.upload.fetchCarelinkData({ - carelinkUsername: creds.username, - carelinkPassword: creds.password, - daysAgo: daysForCareLink, - targetUserId: uploadTargetUser - }, (err, data) => { - if (err) { - let fetchErr = new Error(errorText.E_FETCH_CARELINK); - let fetchErrProps = { - details: err.message, - utc: actionUtils.getUtc(utc), - code: 'E_FETCH_CARELINK', - version: version - }; - dispatch(syncActions.fetchCareLinkFailure(errorText.E_FETCH_CARELINK)); - return dispatch(syncActions.uploadFailure(fetchErr, fetchErrProps, targetDevice)); - } - if (data.search(/302 Moved Temporarily/) !== -1) { - let credsErr = new Error(errorText.E_CARELINK_CREDS); - let credsErrProps = { - utc: actionUtils.getUtc(utc), - code: 'E_CARELINK_CREDS', - version: version - }; - dispatch(syncActions.fetchCareLinkFailure(errorText.E_CARELINK_CREDS)); - return dispatch(syncActions.uploadFailure(credsErr, credsErrProps, targetDevice)); - } - dispatch(syncActions.fetchCareLinkSuccess(uploadTargetUser, deviceKey)); - - const opts = { - targetId: uploadTargetUser, - timezone: targetTimezones[uploadTargetUser], - progress: actionUtils.makeProgressFn(dispatch), - version: version - }; - carelink.upload(data, opts, actionUtils.makeUploadCb(dispatch, getState, 'E_CARELINK_UPLOAD', utc)); - }); - }; -} - -export function doDeviceUpload(driverId, opts = {}, utc) { - return (dispatch, getState) => { - const { device } = services; - const version = versionInfo.semver; - const { devices, os, targetTimezones, uploadTargetUser } = getState(); - const targetDevice = _.find(devices, {source: {driverId: driverId}}); - dispatch(syncActions.deviceDetectRequest()); - _.assign(opts, { - targetId: uploadTargetUser, - timezone: targetTimezones[uploadTargetUser], - progress: actionUtils.makeProgressFn(dispatch), - displayTimeModal: actionUtils.makeDisplayTimeModal(dispatch), - displayAdHocModal: actionUtils.makeDisplayAdhocModal(dispatch), - version: version - }); - const { uploadsByUser } = getState(); - const currentUpload = _.get( - uploadsByUser, - [uploadTargetUser, targetDevice.key], - {} - ); - if (currentUpload.file) { - opts.filedata = currentUpload.file.data; - opts.filename = currentUpload.file.name; - } - - device.detect(driverId, opts, (err, dev) => { - if (err) { - let displayErr = new Error(errorText.E_SERIAL_CONNECTION); - let deviceDetectErrProps = { - details: err.message, - utc: actionUtils.getUtc(utc), - code: 'E_SERIAL_CONNECTION', - version: version - }; - - if (_.get(targetDevice, 'source.driverId', null) === 'Dexcom') { - displayErr = new Error(errorText.E_DEXCOM_CONNECTION); - deviceDetectErrProps.code = 'E_DEXCOM_CONNECTION'; - } - - displayErr.originalError = err; - return dispatch(syncActions.uploadFailure(displayErr, deviceDetectErrProps, targetDevice)); - } - - if (!dev && opts.filename == null) { - let displayErr = new Error(errorText.E_HID_CONNECTION); - let disconnectedErrProps = { - utc: actionUtils.getUtc(utc), - code: 'E_HID_CONNECTION', - version: version - }; - - if (targetDevice.powerOnlyWarning) { - displayErr = new Error(errorText.E_USB_CABLE); - disconnectedErrProps.code = 'E_USB_CABLE'; - } - - if (_.get(targetDevice, 'source.driverId', null) === 'Dexcom') { - displayErr = new Error(errorText.E_DEXCOM_CONNECTION); - disconnectedErrProps.code = 'E_DEXCOM_CONNECTION'; - } - - return dispatch(syncActions.uploadFailure(displayErr, disconnectedErrProps, targetDevice)); - } - - var errorMessage = 'E_DEVICE_UPLOAD'; - if (_.get(targetDevice, 'source.driverId', null) === 'Medtronic') { - errorMessage = 'E_MEDTRONIC_UPLOAD'; - } else if (_.get(targetDevice, 'source.driverId', null) === 'BluetoothLE') { - errorMessage = 'E_BLUETOOTH_PAIR'; - } - device.upload(driverId, opts, actionUtils.makeUploadCb(dispatch, getState, errorMessage , utc)); - }); - }; -} - -export function doUpload (deviceKey, opts, utc) { - return async (dispatch, getState) => { - - const { devices, uploadTargetUser, working } = getState(); - - const targetDevice = _.get(devices, deviceKey); - const driverId = _.get(targetDevice, 'source.driverId'); - const driverManifest = _.get(driverManifests, driverId); - - if (driverManifest && driverManifest.mode === 'serial') { - dispatch(syncActions.uploadRequest(uploadTargetUser, devices[deviceKey], utc)); - - const filters = driverManifest.usb.map(({vendorId, productId}) => ({ - usbVendorId: vendorId, - usbProductId: productId - })); - - try { - const existingPermissions = await navigator.serial.getPorts(); - - for (let i = 0; i < existingPermissions.length; i++) { - const { usbProductId, usbVendorId } = existingPermissions[i].getInfo(); - - for (let j = 0; j < driverManifest.usb.length; j++) { - if (driverManifest.usb[j].vendorId === usbVendorId - && driverManifest.usb[j].productId === usbProductId) { - console.log('Device has already been granted permission'); - opts.port = existingPermissions[i]; - } - } - } - - if (opts.port == null) { - opts.port = await navigator.serial.requestPort({ filters: filters }); - } - } catch (err) { - // not returning error, as we'll attempt user-space driver instead - console.log('Error:', err); - } - } - - if (env.browser && driverManifest && driverManifest.mode === 'HID') { - dispatch(syncActions.uploadRequest(uploadTargetUser, devices[deviceKey], utc)); - - const filters = driverManifest.usb.map(({vendorId, productId}) => ({ - vendorId, - productId - })); - - try { - const existingPermissions = await navigator.hid.getDevices(); - - for (let i = 0; i < existingPermissions.length; i++) { - for (let j = 0; j < driverManifest.usb.length; j++) { - if (driverManifest.usb[j].vendorId === existingPermissions[i].vendorId - && driverManifest.usb[j].productId === existingPermissions[i].productId) { - console.log('Device has already been granted permission'); - opts.hidDevice = existingPermissions[i]; - } - } - } - - if (opts.hidDevice == null) { - [opts.hidDevice] = await navigator.hid.requestDevice({ filters: filters }); - } - - if (opts.hidDevice == null) { - throw new Error('No device was selected.'); - } - } catch (err) { - console.log('Error:', err); - - let hidErr = new Error(errorText.E_HID_CONNECTION); - let errProps = { - details: err.message, - utc: actionUtils.getUtc(utc), - code: 'E_HID_CONNECTION', - }; - - return dispatch(syncActions.uploadFailure(hidErr, errProps, devices[deviceKey])); - } - } - - if (opts && opts.ble) { - // we need to to scan for Bluetooth devices before the version check, - // otherwise it doesn't count as a response to a user request anymore - dispatch(syncActions.uploadRequest(uploadTargetUser, devices[deviceKey], utc)); - console.log('Scanning..'); - try { - await opts.ble.scan(); - } catch (err) { - console.log('Error:', err); - - let btErr = new Error(errorText.E_BLUETOOTH_OFF); - let errProps = { - details: err.message, - utc: actionUtils.getUtc(utc), - code: 'E_BLUETOOTH_OFF', - }; - - return dispatch(syncActions.uploadFailure(btErr, errProps, devices[deviceKey])); - } - console.log('Done.'); - } - - dispatch(syncActions.versionCheckRequest()); - const { api } = services; - const version = versionInfo.semver; - api.upload.getVersions((err, versions) => { - if (err) { - dispatch(syncActions.versionCheckFailure(err)); - return dispatch(syncActions.uploadAborted()); - } - const { uploaderMinimum } = versions; - // if either the version from the jellyfish response - // or the local uploader version is somehow an invalid semver - // we will catch the error and dispatch versionCheckFailure - try { - const upToDate = semver.gte(version, uploaderMinimum); - if (!upToDate) { - dispatch(syncActions.versionCheckFailure(null, version, uploaderMinimum)); - return dispatch(syncActions.uploadAborted()); - } - else { - dispatch(syncActions.versionCheckSuccess()); - } - } - catch(err) { - dispatch(syncActions.versionCheckFailure(err)); - return dispatch(syncActions.uploadAborted()); - } - - if (working.uploading === true) { - return dispatch(syncActions.uploadAborted()); - } - - dispatch(syncActions.uploadRequest(uploadTargetUser, devices[deviceKey], utc)); - - const targetDevice = devices[deviceKey]; - const deviceType = targetDevice.source.type; - - if (_.includes(['device', 'block'], deviceType)) { - dispatch(doDeviceUpload(targetDevice.source.driverId, opts, utc)); - } - else if (deviceType === 'carelink') { - dispatch(doCareLinkUpload(deviceKey, opts, utc)); - } - }); - }; -} - -export function readFile(userId, deviceKey, file, extension) { - return (dispatch, getState) => { - if (!file) { - return; - } - dispatch(syncActions.choosingFile(userId, deviceKey)); - const version = versionInfo.semver; - - if (file.name.slice(-extension.length) !== extension) { - let err = new Error(errorText.E_FILE_EXT + extension); - let errProps = { - code: 'E_FILE_EXT', - version: version - }; - return dispatch(syncActions.readFileAborted(err, errProps)); - } - else { - let reader = new FileReader(); - reader.onloadstart = () => { - dispatch(syncActions.readFileRequest(userId, deviceKey, file.name)); - }; - - reader.onerror = () => { - let err = new Error(errorText.E_READ_FILE + file.name); - let errProps = { - code: 'E_READ_FILE', - version: version - }; - return dispatch(syncActions.readFileFailure(err, errProps)); - }; - - reader.onloadend = ((theFile) => { - return (e) => { - dispatch(syncActions.readFileSuccess(userId, deviceKey, e.srcElement.result)); - dispatch(doUpload(deviceKey)); - }; - })(file); - - reader.readAsArrayBuffer(file); - } - }; -} - -export function doVersionCheck() { - return (dispatch, getState) => { - dispatch(syncActions.versionCheckRequest()); - const { api } = services; - const version = versionInfo.semver; - if(env.browser){ - return dispatch(syncActions.versionCheckSuccess()); - } - api.upload.getVersions((err, versions) => { - if (err) { - return dispatch(syncActions.versionCheckFailure(err)); - } - const { uploaderMinimum } = versions; - // if either the version from the jellyfish response - // or the local uploader version is somehow an invalid semver - // we will catch the error and dispatch versionCheckFailure - try { - const upToDate = semver.gte(version, uploaderMinimum); - if (!upToDate) { - return dispatch(syncActions.versionCheckFailure(null, version, uploaderMinimum)); - } - else { - return dispatch(syncActions.versionCheckSuccess()); - } - } - catch(err) { - return dispatch(syncActions.versionCheckFailure(err)); - } - }); - }; -} - -export function setTargetTimezone(userId, timezoneName) { - return (dispatch, getState) => { - const { allUsers, loggedInUser } = getState(); - const isClinicAccount = personUtils.userHasRole(allUsers[loggedInUser], 'clinic'); - const { api } = services; - dispatch(syncActions.updateProfileRequest()); - let updates = { - patient: { - targetTimezone: timezoneName - } - }; - api.user.updateProfile(userId, updates, (err, profile) => { - // suppress unauthorized error until custodial vs normal permissions are ironed out - // TODO: remove conditional when perms are finalized or accounts are converted - if (err){ - if (_.get(err,'status') !== 401) { - dispatch(syncActions.updateProfileFailure(err)); - } else { - let newProfile = actionUtils.mergeProfileUpdates(allUsers[userId], updates); - dispatch(syncActions.updateProfileSuccess(newProfile, userId)); - } - } else { - dispatch(syncActions.updateProfileSuccess(profile, userId)); - } - if (isClinicAccount) { - return dispatch(syncActions.setTargetTimezone(userId, timezoneName, {metric: {eventName: metrics.CLINIC_TIMEZONE_SELECT}})); - } - return dispatch(syncActions.setTargetTimezone(userId, timezoneName)); - }); - }; -} - -export function clickDeviceSelectionDone() { - return (dispatch, getState) => { - const { targetDevices, uploadTargetUser, allUsers, loggedInUser } = getState(); - const isClinicAccount = personUtils.userHasRole(allUsers[loggedInUser], 'clinic'); - const { api } = services; - dispatch(syncActions.updateProfileRequest()); - if (!_.isEmpty(targetDevices[uploadTargetUser])) { - let updates = { - patient: { - targetDevices: targetDevices[uploadTargetUser] - } - }; - api.user.updateProfile(uploadTargetUser, updates, (err, profile) => { - // suppress unauthorized error until custodial vs normal permissions are ironed out - // TODO: remove conditional when perms are finalized or accounts are converted - if (err) { - if (_.get(err,'status') !== 401) { - dispatch(syncActions.updateProfileFailure(err)); - } else { - let newProfile = actionUtils.mergeProfileUpdates(allUsers[uploadTargetUser], updates); - dispatch(syncActions.updateProfileSuccess(newProfile, uploadTargetUser)); - if (isClinicAccount) { - _.forEach(targetDevices[uploadTargetUser], function(device){ - dispatch(syncActions.clinicAddDevice(device)); - }); - } - } - } else { - dispatch(syncActions.updateProfileSuccess(profile, uploadTargetUser)); - if (isClinicAccount) { - _.forEach(targetDevices[uploadTargetUser], function(device){ - dispatch(syncActions.clinicAddDevice(device)); - }); - } - } - if (isClinicAccount) { - return dispatch(setPage(pages.MAIN, undefined, {metric: {eventName: metrics.CLINIC_DEVICES_DONE}})); - } - return dispatch(setPage(pages.MAIN)); - }); - } - }; -} - -export function clickEditUserNext(profile) { - return (dispatch, getState) => { - const { uploadTargetUser, allUsers } = getState(); - const { api } = services; - const updates = profile; - if (!_.isEmpty(profile)){ - dispatch(syncActions.updateProfileRequest()); - api.user.updateProfile(uploadTargetUser, profile, (err, profile) => { - // suppress unauthorized error until custodial vs normal permissions are ironed out - // TODO: remove conditional when perms are finalized or accounts are converted - if (err) { - if(_.get(err,'status') !== 401) { - return dispatch(syncActions.updateProfileFailure(err)); - } else { - const { allUsers } = getState(); - let newProfile = actionUtils.mergeProfileUpdates(allUsers[uploadTargetUser], updates); - dispatch(syncActions.updateProfileSuccess(newProfile, uploadTargetUser)); - } - } else { - dispatch(syncActions.updateProfileSuccess(profile, uploadTargetUser)); - } - const { targetDevices, devices, allUsers, loggedInUser } = getState(); - const targetedDevices = _.get(targetDevices, uploadTargetUser, []); - const supportedDeviceKeys = _.keys(devices); - const atLeastOneDeviceSupportedOnSystem = _.some(targetedDevices, (key) => { - return _.includes(supportedDeviceKeys, key); - }); - if (_.isEmpty(targetedDevices) || !atLeastOneDeviceSupportedOnSystem) { - return dispatch(setPage(pages.SETTINGS)); - } else { - return dispatch(setPage(pages.MAIN)); - } - }); - } - }; -} - -export function retrieveTargetsFromStorage() { - return (dispatch, getState) => { - const { devices, uploadTargetUser } = getState(); - const { api, localStore } = services; - let fromLocalStore = false; - - dispatch(syncActions.retrieveUsersTargetsFromStorage()); - let targets = localStore.getItem('devices'); - if (targets !== null) { - fromLocalStore = true; - // wipe out the deprecated 'devices' localStore key - localStore.removeItem('devices'); - const uploadsByUser = actionUtils.getDeviceTargetsByUser(targets); - dispatch(syncActions.setUploads(uploadsByUser)); - dispatch(syncActions.setUsersTargets(targets)); - } - - const { targetDevices, targetTimezones, allUsers, loggedInUser } = getState(); - const isClinicAccount = personUtils.userHasRole(allUsers[loggedInUser], 'clinic'); - - if (isClinicAccount) { - return dispatch(setPage(pages.CLINIC_USER_SELECT, null, /*{metric: {eventName: metrics.CLINIC_SEARCH_DISPLAYED}}*/)); - } - // redirect based on having a supported device if not clinic account - if (!_.isEmpty(_.get(targetDevices, uploadTargetUser))) { - let usersWithTargets = {}; - _.forOwn(targetDevices, (devicesArray, userId) => { - usersWithTargets[userId] = _.map(devicesArray, (deviceKey) => { - return {key: deviceKey}; - }); - }); - _.forOwn(targetTimezones, (timezoneName, userId) => { - usersWithTargets[userId] = _.map(usersWithTargets[userId], (target) => { - if (timezoneName != null) { - target.timezone = timezoneName; - } - return target; - }); - }); - const targetDeviceKeys = targetDevices[uploadTargetUser]; - const supportedDeviceKeys = _.keys(devices); - const atLeastOneDeviceSupportedOnSystem = _.some(targetDeviceKeys, (key) => { - return _.includes(supportedDeviceKeys, key); - }); - - if(!fromLocalStore){ - const uploadsByUser = actionUtils.getDeviceTargetsByUser(usersWithTargets); - dispatch(syncActions.setUploads(uploadsByUser)); - } else { - if (!_.isEmpty(targetDevices[uploadTargetUser]) && !_.isEmpty(targetTimezones[uploadTargetUser])) { - dispatch(syncActions.updateProfileRequest()); - const updates = { - patient: { - targetDevices: targetDevices[uploadTargetUser], - targetTimezone: targetTimezones[uploadTargetUser] - } - }; - api.user.updateProfile(uploadTargetUser, updates, (err, profile) => { - // suppress unauthorized error until custodial vs normal permissions are ironed out - // TODO: remove conditional when perms are finalized or accounts are converted - if(err){ - if (_.get(err,'status') !== 401) { - dispatch(syncActions.updateProfileFailure(err)); - } else { - let newProfile = actionUtils.mergeProfileUpdates(allUsers[uploadTargetUser], updates); - dispatch(syncActions.updateProfileSuccess(newProfile, uploadTargetUser)); - } - } else { - dispatch(syncActions.updateProfileSuccess(profile, uploadTargetUser)); - } - }); - } - } - - if (atLeastOneDeviceSupportedOnSystem) { - return dispatch(setPage(pages.MAIN)); - } else { - return dispatch(setPage(pages.SETTINGS)); - } - } else { - return dispatch(setPage(pages.SETTINGS)); - } - }; -} - -export function createCustodialAccount(profile) { - return (dispatch, getState) => { - const { api } = services; - dispatch(syncActions.createCustodialAccountRequest()); - api.user.createCustodialAccount(profile, (err, account) => { - if (err) { - dispatch(syncActions.createCustodialAccountFailure(err)); - } - else { - dispatch(syncActions.createCustodialAccountSuccess(account)); - if (_.get(account, 'profile.patient.mrn', false)) { - dispatch(syncActions.clinicAddMrn()); - } - if (_.get(account, 'profile.patient.email', false)) { - dispatch(syncActions.clinicAddEmail()); - } - dispatch(syncActions.setUploadTargetUser(account.userid)); - dispatch(setPage(pages.SETTINGS)); - } - }); - }; -} - -/* - * COMPLEX ACTION CREATORS - */ - -export function setUploadTargetUserAndMaybeRedirect(targetId) { - return (dispatch, getState) => { - const { devices, targetDevices, allUsers, loggedInUser} = getState(); - dispatch(syncActions.setUploadTargetUser(targetId)); - const { api } = services; - dispatch(syncActions.setBlipViewDataUrl( - api.makeBlipUrl(actionUtils.viewDataPathForUser(targetId)) - )); - const targetedDevices = _.get(targetDevices, targetId, []); - const supportedDeviceKeys = _.keys(devices); - const atLeastOneDeviceSupportedOnSystem = _.some(targetedDevices, (key) => { - return _.includes(supportedDeviceKeys, key); - }); - if (_.isEmpty(targetedDevices) || !atLeastOneDeviceSupportedOnSystem) { - return dispatch(setPage(pages.SETTINGS)); - } - }; -} - -export function checkUploadTargetUserAndMaybeRedirect() { - return (dispatch, getState) => { - const { devices, targetDevices, allUsers, loggedInUser, uploadTargetUser } = getState(); - if (!uploadTargetUser) { - return; - } - const targetedDevices = _.get(targetDevices, uploadTargetUser, []); - const supportedDeviceKeys = _.keys(devices); - const atLeastOneDeviceSupportedOnSystem = _.some(targetedDevices, (key) => { - return _.includes(supportedDeviceKeys, key); - }); - if (_.isEmpty(targetedDevices) || !atLeastOneDeviceSupportedOnSystem) { - return dispatch(setPage(pages.SETTINGS, undefined, {metric: {eventName: metrics.CLINIC_NEXT}})); - } else { - return dispatch(setPage(pages.MAIN, undefined, {metric: {eventName: metrics.CLINIC_NEXT}})); - } - }; -} - -export function clickAddNewUser(){ - return (dispatch, getState) =>{ - dispatch(syncActions.setUploadTargetUser(null)); - dispatch(setPage(pages.CLINIC_USER_EDIT, undefined, {metric: {eventName: metrics.CLINIC_ADD}})); - }; -} - -export function setPage(page, actionSource = actionSources[actionTypes.SET_PAGE], metric) { - return (dispatch, getState) => { - if(pagesMap[page]){ - const meta = { source: actionSource }; - _.assign(meta, metric); - dispatch(push({pathname: pagesMap[page], state: { meta }})); - } - }; -} diff --git a/app/actions/index.js b/app/actions/index.js deleted file mode 100644 index 859a62bf8..000000000 --- a/app/actions/index.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2015, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import * as async from './async'; -import * as sync from './sync'; - -export default { - async: async, - sync: sync -}; diff --git a/app/actions/sync.js b/app/actions/sync.js deleted file mode 100644 index 75aabb2d4..000000000 --- a/app/actions/sync.js +++ /dev/null @@ -1,842 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2015, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; - -import * as actionTypes from '../constants/actionTypes'; -import * as actionSources from '../constants/actionSources'; -import * as metrics from '../constants/metrics'; - -import { - addInfoToError, - getAppInitErrorMessage, - getLoginErrorMessage, - getLogoutErrorMessage, - getUpdateProfileErrorMessage, - getCreateCustodialAccountErrorMessage, - UnsupportedError -} from '../utils/errors'; -import errorText from '../constants/errors'; - -import * as actionUtils from './utils'; -import personUtils from '../../lib/core/personUtils'; -import uploadDataPeriod from '../utils/uploadDataPeriod'; - -const uploadDataPeriodLabels = { - [uploadDataPeriod.PERIODS.ALL]: 'all data', - [uploadDataPeriod.PERIODS.DELTA]: 'new data', - [uploadDataPeriod.PERIODS.FOUR_WEEKS]: '4 weeks' -}; - -export function addTargetDevice(userId, deviceKey) { - return { - type: actionTypes.ADD_TARGET_DEVICE, - payload: { userId, deviceKey }, - meta: {source: actionSources[actionTypes.ADD_TARGET_DEVICE]} - }; -} - -// NB: this action exists purely to trigger the metrics middleware -// no reducer responds to it to adjust any state! -export function clickGoToBlip() { - return { - type: actionTypes.CLICK_GO_TO_BLIP, - meta: { - source: actionSources[actionTypes.CLICK_GO_TO_BLIP], - metric: {eventName: metrics.CLICK_GO_TO_BLIP} - } - }; -} - -export function rememberMedtronicSerialNumber(serialNumber) { - return { - type: actionTypes.MEDTRONIC_REMEMBER_SERIAL_NUMBER, - meta: { - source: actionSources[actionTypes.MEDTRONIC_REMEMBER_SERIAL_NUMBER], - metric: { eventName: metrics.MEDTRONIC_REMEMBER_SERIAL_NUMBER } - } - }; -} - -export function clinicAddMrn(){ - return { - type: actionTypes.CLINIC_ADD_MRN, - meta: { - source: actionSources[actionTypes.CLINIC_ADD_MRN], - metric: {eventName: metrics.CLINIC_ADD_MRN} - } - }; -} - -export function clinicAddEmail(){ - return { - type: actionTypes.CLINIC_ADD_EMAIL, - meta: { - source: actionSources[actionTypes.CLINIC_ADD_EMAIL], - metric: {eventName: metrics.CLINIC_ADD_EMAIL} - } - }; -} - -export function clinicAddDevice(deviceKey){ - return { - type: actionTypes.CLINIC_DEVICE_STORED, - meta: { - source: actionSources[actionTypes.CLINIC_DEVICE_STORED], - metric: {eventName: metrics.CLINIC_DEVICE_STORED + ' - ' + deviceKey} - } - }; -} - -export function clinicInvalidDate(errors){ - if (_.get(errors, 'year', false)) { - return { - type: actionTypes.CLINIC_ADD_INVALID_DATE, - meta: { - source: actionSources[actionTypes.CLINIC_ADD_INVALID_DATE], - metric: {eventName: metrics.CLINIC_ADD_INVALID_DATE} - } - }; - } -} - -export function hideUnavailableDevices(os) { - return { - type: actionTypes.HIDE_UNAVAILABLE_DEVICES, - payload: { os }, - meta: {source: actionSources[actionTypes.HIDE_UNAVAILABLE_DEVICES]} - }; -} - -export function removeTargetDevice(userId, deviceKey) { - return { - type: actionTypes.REMOVE_TARGET_DEVICE, - payload: { userId, deviceKey }, - meta: {source: actionSources[actionTypes.REMOVE_TARGET_DEVICE]} - }; -} - -export function resetUpload(userId, deviceKey) { - return { - type: actionTypes.RESET_UPLOAD, - payload: { userId, deviceKey }, - meta: {source: actionSources[actionTypes.RESET_UPLOAD]} - }; -} - -export function setBlipViewDataUrl(url) { - return { - type: actionTypes.SET_BLIP_VIEW_DATA_URL, - payload: { url }, - meta: {source: actionSources[actionTypes.SET_BLIP_VIEW_DATA_URL]} - }; -} - -export function setForgotPasswordUrl(url) { - return { - type: actionTypes.SET_FORGOT_PASSWORD_URL, - payload: { url }, - meta: {source: actionSources[actionTypes.SET_FORGOT_PASSWORD_URL]} - }; -} - -export function setNewPatientUrl(url) { - return { - type: actionTypes.SET_NEW_PATIENT_URL, - payload: { url }, - meta: {source: actionSources[actionTypes.SET_NEW_PATIENT_URL]} - }; -} - -export function setSignUpUrl(url) { - return { - type: actionTypes.SET_SIGNUP_URL, - payload: { url }, - meta: {source: actionSources[actionTypes.SET_SIGNUP_URL]} - }; -} - -export function setTargetTimezone(userId, timezoneName, metric) { - let meta = {source: actionSources[actionTypes.SET_TARGET_TIMEZONE]}; - if (metric) { - _.assign(meta, metric); - } - return { - type: actionTypes.SET_TARGET_TIMEZONE, - payload: { userId, timezoneName }, - meta: meta - }; -} - -export function setUploads(devicesByUser) { - return { - type: actionTypes.SET_UPLOADS, - payload: { devicesByUser }, - meta: {source: actionSources[actionTypes.SET_UPLOADS]} - }; -} - -export function setUploadTargetUser(userId, metric) { - let meta = {source: actionSources[actionTypes.SET_UPLOAD_TARGET_USER]}; - if (metric) { - _.assign(meta, {metric}); - } - return { - type: actionTypes.SET_UPLOAD_TARGET_USER, - payload: { userId }, - meta - }; -} - -export function toggleDropdown(previous, actionSource = actionSources[actionTypes.TOGGLE_DROPDOWN]) { - return { - type: actionTypes.TOGGLE_DROPDOWN, - payload: { isVisible: !previous }, - meta: {source: actionSource} - }; -} - -export function toggleErrorDetails(userId, deviceKey, previous) { - if (_.includes([null, undefined], previous)) { - previous = false; - } - return { - type: actionTypes.TOGGLE_ERROR_DETAILS, - payload: { isVisible: !previous, userId, deviceKey }, - meta: {source: actionSources[actionTypes.TOGGLE_ERROR_DETAILS]} - }; -} - -export function dismissUpdateProfileError(){ - return { - type: actionTypes.DISMISS_UPDATE_PROFILE_ERROR, - meta: {source: actionSources[actionTypes.DISMISS_UPDATE_PROFILE_ERROR]} - }; -} - -export function dismissCreateCustodialAccountError(){ - return { - type: actionTypes.DISMISS_CREATE_CUSTODIAL_ACCOUNT_ERROR, - meta: {source: actionSources[actionTypes.DISMISS_CREATE_CUSTODIAL_ACCOUNT_ERROR]} - }; -} - -export function setAllUsers(user, profile, memberships){ - return { - type: actionTypes.SET_ALL_USERS, - payload: { memberships: memberships, user: user, profile: profile }, - meta: {source: actionSources[actionTypes.SET_ALL_USERS]} - }; -} - -/* - * relating to async action creator doAppInit - */ - -export function initRequest() { - return { - type: actionTypes.INIT_APP_REQUEST, - meta: {source: actionSources[actionTypes.INIT_APP_REQUEST]} - }; -} - -export function initSuccess() { - return { - type: actionTypes.INIT_APP_SUCCESS, - meta: {source: actionSources[actionTypes.INIT_APP_SUCCESS]} - }; -} - -export function initFailure(err) { - const error = new Error(getAppInitErrorMessage(err.status || null)); - error.originalError = err; - return { - type: actionTypes.INIT_APP_FAILURE, - error: true, - payload: error, - meta: {source: actionSources[actionTypes.INIT_APP_FAILURE]} - }; -} - -export function setUserInfoFromToken(results) { - const { user, profile, memberships } = results; - return { - type: actionTypes.SET_USER_INFO_FROM_TOKEN, - payload: { user, profile, memberships }, - meta: {source: actionSources[actionTypes.SET_USER_INFO_FROM_TOKEN]} - }; -} - -/* - * relating to async action creator doLogin - */ - -export function loginRequest() { - return { - type: actionTypes.LOGIN_REQUEST, - meta: {source: actionSources[actionTypes.LOGIN_REQUEST]} - }; -} - -export function loginSuccess(results) { - const { user, profile, memberships } = results; - const isClinicAccount = personUtils.userHasRole(user, 'clinic'); - if (isClinicAccount) { - uploadDataPeriod.setPeriodMedtronic600(uploadDataPeriod.PERIODS.FOUR_WEEKS); - } - return { - type: actionTypes.LOGIN_SUCCESS, - payload: { user, profile, memberships }, - meta: { - source: actionSources[actionTypes.LOGIN_SUCCESS], - metric: {eventName: isClinicAccount ? metrics.CLINIC_LOGIN_SUCCESS : metrics.LOGIN_SUCCESS} - } - }; -} - -export function loginFailure(errorCode) { - return { - type: actionTypes.LOGIN_FAILURE, - error: true, - payload: new Error(getLoginErrorMessage(errorCode)), - meta: {source: actionSources[actionTypes.LOGIN_FAILURE]} - }; -} - -/* - * relating to async action creator doLogout - */ - -export function logoutRequest() { - return { - type: actionTypes.LOGOUT_REQUEST, - meta: { - source: actionSources[actionTypes.LOGOUT_REQUEST], - metric: {eventName: metrics.LOGOUT_REQUEST} - } - }; -} - -export function logoutSuccess() { - return { - type: actionTypes.LOGOUT_SUCCESS, - meta: {source: actionSources[actionTypes.LOGOUT_SUCCESS]} - }; -} - -export function logoutFailure() { - return { - type: actionTypes.LOGOUT_FAILURE, - error: true, - payload: new Error(getLogoutErrorMessage()), - meta: {source: actionSources[actionTypes.LOGOUT_FAILURE]} - }; -} - -/* - * relating to async action creator doCareLinkUpload - */ - -export function fetchCareLinkRequest(userId, deviceKey) { - return { - type: actionTypes.CARELINK_FETCH_REQUEST, - payload: { userId, deviceKey }, - meta: {source: actionSources[actionTypes.CARELINK_FETCH_REQUEST]} - }; -} - -export function fetchCareLinkSuccess(userId, deviceKey) { - return { - type: actionTypes.CARELINK_FETCH_SUCCESS, - payload: { userId, deviceKey }, - meta: { - source: actionSources[actionTypes.CARELINK_FETCH_SUCCESS], - metric: {eventName: metrics.CARELINK_FETCH_SUCCESS} - } - }; -} - -export function fetchCareLinkFailure(message) { - return { - type: actionTypes.CARELINK_FETCH_FAILURE, - error: true, - payload: new Error(message), - meta: { - source: actionSources[actionTypes.CARELINK_FETCH_FAILURE], - metric: {eventName: metrics.CARELINK_FETCH_FAILURE} - } - }; -} - -/* - * relating to async action creator doUpload - */ - -export function uploadAborted() { - return { - type: actionTypes.UPLOAD_ABORTED, - error: true, - payload: new Error(errorText.E_UPLOAD_IN_PROGRESS), - meta: {source: actionSources[actionTypes.UPLOAD_ABORTED]} - }; -} - -export function uploadRequest(userId, device, utc) { - utc = actionUtils.getUtc(utc); - const properties = { - type: _.get(device, 'source.type', undefined), - source: `${actionUtils.getUploadTrackingId(device)}` - }; - if (_.get(device, 'source.driverId', null) === 'Medtronic600') { - _.extend(properties, { 'limit': uploadDataPeriodLabels[uploadDataPeriod.periodMedtronic600] }); - } - return { - type: actionTypes.UPLOAD_REQUEST, - payload: { userId, deviceKey: device.key, utc }, - meta: { - source: actionSources[actionTypes.UPLOAD_REQUEST], - metric: { - eventName: `${metrics.UPLOAD_REQUEST}`, - properties - } - } - }; -} - -export function uploadProgress(step, percentage, isFirstUpload) { - return { - type: actionTypes.UPLOAD_PROGRESS, - payload: { step, percentage, isFirstUpload }, - meta: {source: actionSources[actionTypes.UPLOAD_PROGRESS]} - }; -} - -export function uploadSuccess(userId, device, upload, data, utc) { - utc = actionUtils.getUtc(utc); - const numRecs = _.get(data, 'post_records.length', undefined); - const properties = { - type: _.get(device, 'source.type', undefined), - deviceModel: _.get(data, 'deviceModel', undefined), - source: `${actionUtils.getUploadTrackingId(device)}`, - started: upload.history[0].start || '', - finished: utc || '', - processed: numRecs || 0 - }; - if (_.get(device, 'source.driverId', null) === 'Medtronic600') { - _.extend(properties, { 'limit': uploadDataPeriodLabels[uploadDataPeriod.periodMedtronic600] }); - } - return { - type: actionTypes.UPLOAD_SUCCESS, - payload: { userId, deviceKey: device.key, data, utc }, - meta: { - source: actionSources[actionTypes.UPLOAD_SUCCESS], - metric: { - eventName: `${metrics.UPLOAD_SUCCESS}`, - properties - } - } - }; -} - -export function uploadFailure(err, errProps, device) { - err = addInfoToError(err, errProps); - const properties = { - type: _.get(device, 'source.type', undefined), - source: `${actionUtils.getUploadTrackingId(device)}`, - error: err - }; - if (_.get(device, 'source.driverId', null) === 'Medtronic600') { - _.extend(properties, { 'limit': uploadDataPeriodLabels[uploadDataPeriod.periodMedtronic600] }); - } - return { - type: actionTypes.UPLOAD_FAILURE, - error: true, - payload: err, - meta: { - source: actionSources[actionTypes.UPLOAD_FAILURE], - metric: { - eventName: `${metrics.UPLOAD_FAILURE}`, - properties - } - } - }; -} - -export function uploadCancelled(utc) { - return { - type: actionTypes.UPLOAD_CANCELLED, - payload: { utc }, - meta: { - source: actionSources[actionTypes.UPLOAD_CANCELLED] - } - }; -} - -export function deviceDetectRequest() { - return { - type: actionTypes.DEVICE_DETECT_REQUEST, - meta: {source: actionSources[actionTypes.DEVICE_DETECT_REQUEST]} - }; -} - -/* - * relating to async action creator readFile - */ - -export function choosingFile(userId, deviceKey) { - return { - type: actionTypes.CHOOSING_FILE, - payload: { userId, deviceKey }, - meta: {source: actionSources[actionTypes.CHOOSING_FILE]} - }; -} - -export function readFileAborted(err, errProps) { - return { - type: actionTypes.READ_FILE_ABORTED, - error: true, - payload: addInfoToError(err, errProps), - meta: {source: actionSources[actionTypes.READ_FILE_ABORTED]} - }; -} - -export function readFileRequest(userId, deviceKey, filename) { - return { - type: actionTypes.READ_FILE_REQUEST, - payload: { userId, deviceKey, filename }, - meta: {source: actionSources[actionTypes.READ_FILE_REQUEST]} - }; -} - -export function readFileSuccess(userId, deviceKey, filedata) { - return { - type: actionTypes.READ_FILE_SUCCESS, - payload: { userId, deviceKey, filedata }, - meta: {source: actionSources[actionTypes.READ_FILE_SUCCESS]} - }; -} - -export function readFileFailure(err, errProps) { - return { - type: actionTypes.READ_FILE_FAILURE, - error: true, - payload: addInfoToError(err, errProps), - meta: {source: actionSources[actionTypes.READ_FILE_FAILURE]} - }; -} - -/* - * relating to async action creator doVersionCheck - */ - -export function versionCheckRequest() { - return { - type: actionTypes.VERSION_CHECK_REQUEST, - meta: {source: actionSources[actionTypes.VERSION_CHECK_REQUEST]} - }; -} - -export function versionCheckSuccess() { - return { - type: actionTypes.VERSION_CHECK_SUCCESS, - meta: {source: actionSources[actionTypes.VERSION_CHECK_SUCCESS]} - }; -} - -export function versionCheckFailure(err, currentVersion, requiredVersion) { - if (err != null) { - return { - type: actionTypes.VERSION_CHECK_FAILURE, - error: true, - payload: err, - meta: { - source: actionSources[actionTypes.VERSION_CHECK_FAILURE], - metric: { - eventName: metrics.UNSUPPORTED_SCREEN_DISPLAYED - } - } - }; - } - else { - return { - type: actionTypes.VERSION_CHECK_FAILURE, - error: true, - payload: new UnsupportedError(currentVersion, requiredVersion), - meta: { - source: actionSources[actionTypes.VERSION_CHECK_FAILURE], - metric: { - eventName: metrics.VERSION_CHECK_FAILURE_OUTDATED, - properties: { requiredVersion } - } - } - }; - } -} - -/* - * relating to updateProfile - */ - -export function updateProfileRequest() { - return { - type: actionTypes.UPDATE_PROFILE_REQUEST, - meta: {source: actionSources[actionTypes.UPDATE_PROFILE_REQUEST]} - }; -} - -export function updateProfileSuccess(profile, userId) { - return { - type: actionTypes.UPDATE_PROFILE_SUCCESS, - payload: { profile, userId }, - meta: {source: actionSources[actionTypes.UPDATE_PROFILE_SUCCESS]} - }; -} - -export function updateProfileFailure(err) { - const error = new Error(getUpdateProfileErrorMessage(err.status || null)); - error.originalError = err; - return { - type: actionTypes.UPDATE_PROFILE_FAILURE, - error: true, - payload: error, - meta: {source: actionSources[actionTypes.UPDATE_PROFILE_FAILURE]} - }; -} - -/* - * relating to createCustodialAccount - */ - -export function createCustodialAccountRequest() { - return { - type: actionTypes.CREATE_CUSTODIAL_ACCOUNT_REQUEST, - meta: {source: actionSources[actionTypes.CREATE_CUSTODIAL_ACCOUNT_REQUEST]} - }; -} - -export function createCustodialAccountSuccess(account) { - return { - type: actionTypes.CREATE_CUSTODIAL_ACCOUNT_SUCCESS, - payload: { account }, - meta: { - source: actionSources[actionTypes.CREATE_CUSTODIAL_ACCOUNT_SUCCESS], - metric: {eventName: metrics.CLINIC_ADD_NEW_PATIENT} - } - }; -} - -export function createCustodialAccountFailure(err) { - const error = new Error(getCreateCustodialAccountErrorMessage(err.status || null)); - error.originalError = err; - return { - type: actionTypes.CREATE_CUSTODIAL_ACCOUNT_FAILURE, - error: true, - payload: error, - meta: {source: actionSources[actionTypes.CREATE_CUSTODIAL_ACCOUNT_FAILURE]} - }; -} - -/* - * relating to side-effect-performing action creators - * retrieveTargetsFromStorage - */ - -export function retrieveUsersTargetsFromStorage() { - return { - type: actionTypes.RETRIEVING_USERS_TARGETS, - meta: {source: actionSources[actionTypes.RETRIEVING_USERS_TARGETS]} - }; -} - -export function setUsersTargets(targets) { - return { - type: actionTypes.SET_USERS_TARGETS, - payload: { targets }, - meta: {source: actionSources[actionTypes.SET_USERS_TARGETS]} - }; -} - -/* - * relating to electron auto-updater - */ - -export function autoCheckingForUpdates() { - return { - type: actionTypes.AUTO_UPDATE_CHECKING_FOR_UPDATES, - meta: { source: actionSources[actionTypes.AUTO_UPDATE_CHECKING_FOR_UPDATES] } - }; -} - -export function manualCheckingForUpdates() { - return { - type: actionTypes.MANUAL_UPDATE_CHECKING_FOR_UPDATES, - meta: { source: actionSources[actionTypes.MANUAL_UPDATE_CHECKING_FOR_UPDATES] } - }; -} - -export function updateAvailable(info) { - return { - type: actionTypes.UPDATE_AVAILABLE, - payload: { info }, - meta: { source: actionSources[actionTypes.UPDATE_AVAILABLE] } - }; -} - -export function updateNotAvailable(info) { - return { - type: actionTypes.UPDATE_NOT_AVAILABLE, - payload: { info }, - meta: { source: actionSources[actionTypes.UPDATE_NOT_AVAILABLE] } - }; -} - -export function autoUpdateError(error) { - return { - type: actionTypes.AUTOUPDATE_ERROR, - payload: { error }, - meta: { source: actionSources[actionTypes.AUTOUPDATE_ERROR] } - }; -} - -export function updateDownloaded(info) { - return { - type: actionTypes.UPDATE_DOWNLOADED, - payload: { info }, - meta: { source: actionSources[actionTypes.UPDATE_DOWNLOADED] } - }; -} - -export function dismissUpdateAvailable() { - return { - type: actionTypes.DISMISS_UPDATE_AVAILABLE, - meta: { source: actionSources[actionTypes.DISMISS_UPDATE_AVAILABLE] } - }; -} - -export function dismissUpdateNotAvailable() { - return { - type: actionTypes.DISMISS_UPDATE_NOT_AVAILABLE, - meta: { source: actionSources[actionTypes.DISMISS_UPDATE_NOT_AVAILABLE] } - }; -} - -export function quitAndInstall() { - return { - type: actionTypes.QUIT_AND_INSTALL, - meta: { - source: actionSources[actionTypes.QUIT_AND_INSTALL], - metric: { eventName: metrics.QUIT_AND_INSTALL } - } - }; -} - -/* - * relating to driver updates - */ - -export function checkingForDriverUpdate() { - return { - type: actionTypes.CHECKING_FOR_DRIVER_UPDATE, - meta: { source: actionSources[actionTypes.CHECKING_FOR_DRIVER_UPDATE] } - }; -} - -export function driverUpdateAvailable(current, available) { - return { - type: actionTypes.DRIVER_UPDATE_AVAILABLE, - payload: { current, available }, - meta: { source: actionSources[actionTypes.DRIVER_UPDATE_AVAILABLE] } - }; -} - -export function driverUpdateNotAvailable() { - return { - type: actionTypes.DRIVER_UPDATE_NOT_AVAILABLE, - meta: { source: actionSources[actionTypes.DRIVER_UPDATE_NOT_AVAILABLE] } - }; -} - -export function dismissDriverUpdateAvailable() { - return { - type: actionTypes.DISMISS_DRIVER_UPDATE_AVAILABLE, - meta: { source: actionSources[actionTypes.DISMISS_DRIVER_UPDATE_AVAILABLE] } - }; -} - -export function driverInstall() { - return { - type: actionTypes.DRIVER_INSTALL, - meta: { - source: actionSources[actionTypes.DRIVER_INSTALL] - } - }; -} - -export function driverUpdateShellOpts(opts) { - return { - type: actionTypes.DRIVER_INSTALL_SHELL_OPTS, - payload: { opts }, - meta: {source: actionSources[actionTypes.DRIVER_INSTALL_SHELL_OPTS] } - }; -} - -export function deviceTimeIncorrect(callback, cfg, times) { - return { - type: actionTypes.DEVICE_TIME_INCORRECT, - payload: { callback, cfg, times }, - meta: { - source: actionSources[actionTypes.DEVICE_TIME_INCORRECT], - metric: { - eventName: metrics.DEVICE_TIME_INCORRECT, - properties: { times }, - } - }, - }; -} - -export function dismissedDeviceTimePrompt() { - return { - type: actionTypes.DISMISS_DEVICE_TIME_PROMPT, - meta: { source: actionSources[actionTypes.DISMISS_DEVICE_TIME_PROMPT] } - }; -} - -export function timezoneBlur() { - return { - type: actionTypes.TIMEZONE_BLUR, - meta: { source: actionSources[actionTypes.TIMEZONE_BLUR] } - }; -} - -/* -* relating to ad hoc pairing dialog -*/ - -export function adHocPairingRequest(callback, cfg) { - return { - type: actionTypes.AD_HOC_PAIRING_REQUEST, - payload: { callback, cfg }, - meta: { source: actionSources[actionTypes.AD_HOC_PAIRING_REQUEST] } - }; -} - -export function dismissedAdHocPairingDialog() { - return { - type: actionTypes.AD_HOC_PAIRING_DISMISSED, - meta: { source: actionSources[actionTypes.AD_HOC_PAIRING_DISMISSED] } - }; -} diff --git a/app/actions/utils.js b/app/actions/utils.js deleted file mode 100644 index aefc09685..000000000 --- a/app/actions/utils.js +++ /dev/null @@ -1,133 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2015, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; -import stacktrace from 'stack-trace'; - -import sundial from 'sundial'; - -import errorText from '../constants/errors'; -import * as syncActions from './sync'; - -const isBrowser = typeof window !== 'undefined'; -// eslint-disable-next-line no-console -const debug = isBrowser ? require('bows')('utils') : console.log; - -export function getDeviceTargetsByUser(targetsByUser) { - return _.mapValues(targetsByUser, (targets) => { - return _.map(targets, 'key'); - }); -} - -export function getUploadTrackingId(device) { - const source = device.source; - if (source.type === 'device' || source.type === 'block') { - return source.driverId; - } - if (source.type === 'carelink') { - return 'CareLink'; - } - return null; -} - -export function getUtc(utc) { - return _.isEmpty(utc) ? sundial.utcDateString() : utc; -} - -export function makeProgressFn(dispatch) { - return (step, percentage, isFirstUpload) => { - dispatch(syncActions.uploadProgress(step, percentage, isFirstUpload)); - }; -} - -export function makeDisplayTimeModal(dispatch) { - return (cb, cfg, times) => { - dispatch(syncActions.deviceTimeIncorrect(cb, cfg, times)); - }; -} - -export function makeDisplayAdhocModal(dispatch) { - return (cb, cfg) => { - dispatch(syncActions.adHocPairingRequest(cb, cfg)); - }; -} - -export function makeUploadCb(dispatch, getState, errCode, utc) { - return (err, recs) => { - const { devices, uploadsByUser, uploadTargetDevice, uploadTargetUser, version } = getState(); - const targetDevice = devices[uploadTargetDevice]; - - if (err) { - if(err === 'deviceTimePromptClose'){ - return dispatch(syncActions.uploadCancelled(getUtc(utc))); - } - // the drivers sometimes just pass a string arg as err, instead of an actual error :/ - if (typeof err === 'string') { - err = new Error(err); - } - const serverErr = 'Origin is not allowed by Access-Control-Allow-Origin'; - let displayErr = new Error(err.message === serverErr ? - errorText.E_SERVER_ERR : errorText[err.code || errCode]); - let uploadErrProps = { - details: err.message, - utc: getUtc(utc), - name: err.name || 'Uncaught or API POST error', - step: err.step || null, - datasetId: err.datasetId || null, - requestTrace: err.requestTrace || null, - sessionTrace: err.sessionTrace || null, - sessionToken: err.sessionToken || null, - code: err.code || errCode, - version: version, - data: recs - }; - displayErr.originalError = err; - - if (errCode === 'E_BLUETOOTH_PAIR') { - displayErr.message = 'Couldn\'t connect to device.'; - displayErr.link = 'https://support.tidepool.org/hc/en-us/articles/360035332972'; - displayErr.linkText = 'Is it paired?'; - } - - if (!(process.env.NODE_ENV === 'test')) { - uploadErrProps.stringifiedStack = _.map( - _.filter( - stacktrace.parse(err), - (cs) => { return cs.functionName !== null; } - ), - 'functionName' - ).join(', '); - } - return dispatch(syncActions.uploadFailure(displayErr, uploadErrProps, targetDevice)); - } - const currentUpload = _.get(uploadsByUser, [uploadTargetUser, targetDevice.key], {}); - dispatch(syncActions.uploadSuccess(uploadTargetUser, targetDevice, currentUpload, recs, utc)); - }; -} - -export function viewDataPathForUser(uploadTargetUser) { - return `/patients/${uploadTargetUser}/data`; -} - -export function mergeProfileUpdates(profile, updates){ - // merge property values except arrays, which get replaced entirely - return _.mergeWith(profile, updates, (original, update) => { - if (_.isArray(original)) { - return update; - } - }); -} diff --git a/app/app.global.css b/app/app.global.css deleted file mode 100755 index e69de29bb..000000000 diff --git a/app/app.html b/app/app.html deleted file mode 100755 index 377ce7f0f..000000000 --- a/app/app.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - Tidepool Uploader - - - -
- - - diff --git a/app/app.icns b/app/app.icns deleted file mode 100755 index 4f3cbbafa..000000000 Binary files a/app/app.icns and /dev/null differ diff --git a/app/components/AdHocModal.js b/app/components/AdHocModal.js deleted file mode 100644 index 1746794e7..000000000 --- a/app/components/AdHocModal.js +++ /dev/null @@ -1,89 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2014-2016, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ - -import _ from 'lodash'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; - -import { sync as syncActions } from '../actions/'; - -import styles from '../../styles/components/AdHocModal.module.less'; -import step1_img from '../../images/adhoc_s1.png'; -import step2_img from '../../images/adhoc_s2.png'; - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -export class AdHocModal extends Component { - handleContinue = () => { - const { showingAdHocPairingDialog, sync } = this.props; - showingAdHocPairingDialog.callback('adHocModalClose'); - sync.dismissedAdHocPairingDialog(); - } - - render() { - const { showingAdHocPairingDialog } = this.props; - - if(!showingAdHocPairingDialog){ - return null; - } - - return ( -
-
-
-
{i18n.t('Allow the connection on the pump:')}
-
-
-
-
-
-
1. {i18n.t('Scroll down')}
-
-
-
-
2. {i18n.t('Select \"Yes\"')}
-
-
-
-
-
-
- -
-
-
- ); - } -}; - -export default connect( - (state, ownProps) => { - return { - showingAdHocPairingDialog: state.showingAdHocPairingDialog - }; - }, - (dispatch) => { - return { - sync: bindActionCreators(syncActions, dispatch) - }; - } -)(AdHocModal); diff --git a/app/components/ClinicUploadDone.js b/app/components/ClinicUploadDone.js deleted file mode 100644 index 5a1fffc0a..000000000 --- a/app/components/ClinicUploadDone.js +++ /dev/null @@ -1,56 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2014, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ - -var React = require('react'); -var _ = require('lodash'); -var PropTypes = require('prop-types'); - -var styles = require('../../styles/components/ClinicUploadDone.module.less'); - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -class ClinicUploadDone extends React.Component { - static propTypes = { - onClicked: PropTypes.func.isRequired, - uploadTargetUser: PropTypes.string.isRequired, - uploadsByUser: PropTypes.object.isRequired - }; - - handleClick = () => { - this.props.onClicked(); - }; - - hasCompletedUpload = () => { - return _.find(_.get(this.props.uploadsByUser, this.props.uploadTargetUser, {}), {completed: true}); - }; - - render() { - return ( -
- - {i18n.t('Done')} - -
- ); - } -} - -module.exports = ClinicUploadDone; diff --git a/app/components/ClinicUserBlock.js b/app/components/ClinicUserBlock.js deleted file mode 100644 index 11f8cc05e..000000000 --- a/app/components/ClinicUserBlock.js +++ /dev/null @@ -1,78 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2014, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ - -var _ = require('lodash'); -var PropTypes = require('prop-types'); -var React = require('react'); -var sundial = require('sundial'); -var personUtils = require('../../lib/core/personUtils'); -var cx = require('classnames'); - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -var styles = require('../../styles/components/ClinicUserBlock.module.less'); - -class ClinicUserBlock extends React.Component { - static propTypes = { - allUsers: PropTypes.object.isRequired, - memberships: PropTypes.object.isRequired, - targetId: PropTypes.string, - timezoneDropdown: PropTypes.element, - onEditUser: PropTypes.func.isRequired, - isUploadInProgress: PropTypes.bool.isRequired - }; - - formatBirthday = (birthday) => { - return sundial.translateMask(birthday, 'YYYY-MM-DD', 'M/D/YYYY'); - }; - - noopHandler = (e) => { - e.preventDefault(); - }; - - render() { - var { allUsers, isUploadInProgress, memberships, targetId } = this.props; - var isCustodialAccount = _.has(_.get(memberships, [targetId, 'permissions']), 'custodian'); - var editClasses = cx({ - [styles.edit]: true, - [styles.disabled]: isUploadInProgress - }); - - return ( -
-
-
- {personUtils.patientFullName(_.get(allUsers, targetId))} -
-
- {this.formatBirthday(_.get(allUsers, [targetId, 'patient', 'birthday']))} -
- {isCustodialAccount && -
- {i18n.t('Edit Info')} -
- } -
- {this.props.timezoneDropdown} -
- ); - } -} - -module.exports = ClinicUserBlock; diff --git a/app/components/ClinicUserEdit.js b/app/components/ClinicUserEdit.js deleted file mode 100644 index e0cb6a524..000000000 --- a/app/components/ClinicUserEdit.js +++ /dev/null @@ -1,275 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2014, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import { reduxForm, Field, Fields } from 'redux-form'; -import { connect } from 'react-redux'; - -var React = require('react'); -var PropTypes = require('prop-types'); -var _ = require('lodash'); -var sundial = require('sundial'); -var personUtils = require('../../lib/core/personUtils'); -var styles = require('../../styles/components/ClinicUserEdit.module.less'); - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -function zeroPad(value){ - return _.padStart(value, 2, '0'); -} - -function validateForm(values){ - var errors = {}; - if(!values.fullName){ - errors.fullName = i18n.t('Your patient\'s full name is needed'); - } - if(values.year && values.month && values.day){ - if(!isValidDate(values.year + '-' + values.month + '-' + zeroPad(values.day))){ - errors.year = i18n.t('Hmm, this date doesn’t look right'); - } - } else { - errors.year = i18n.t('Hmm, this date doesn’t look right'); - } - return errors; -} - -function isValidDate(dateString){ - // check to see if date is proper and not in the future - return (sundial.isValidDateForMask(dateString, 'YYYY-MM-DD')) && - (sundial.dateDifference(new Date(), dateString, 'd') > 0); -} - -var MONTHS = [ - {value: '', label: i18n.t('Month')}, - {value: '01', label: i18n.t('January')}, - {value: '02', label: i18n.t('February')}, - {value: '03', label: i18n.t('March')}, - {value: '04', label: i18n.t('April')}, - {value: '05', label: i18n.t('May')}, - {value: '06', label: i18n.t('June')}, - {value: '07', label: i18n.t('July')}, - {value: '08', label: i18n.t('August')}, - {value: '09', label: i18n.t('September')}, - {value: '10', label: i18n.t('October')}, - {value: '11', label: i18n.t('November')}, - {value: '12', label: i18n.t('December')} -]; - -var options = _.map(MONTHS, function(item) { - return ; -}); - -function renderInput(field){ - return ( -
- - {field.meta.touched && - field.meta.error && -
{field.meta.error}
} -
- ); -}; - -class ClinicUserEdit extends React.Component { - static propTypes = { - createCustodialAccountErrorMessage: PropTypes.string, - createCustodialAccountErrorDismissed: PropTypes.bool.isRequired, - dismissCreateCustodialAccountError: PropTypes.func.isRequired, - updateProfileErrorMessage: PropTypes.string, - updateProfileErrorDismissed: PropTypes.bool.isRequired, - dismissUpdateProfileError: PropTypes.func.isRequired, - allUsers: PropTypes.object.isRequired, - loggedInUser: PropTypes.string.isRequired, - targetId: PropTypes.string, - updateUser: PropTypes.func.isRequired, - createUser: PropTypes.func.isRequired, - cancelEdit: PropTypes.func.isRequired, - onSubmitFail: PropTypes.func.isRequired - }; - - handleCancel = () => { - this.props.cancelEdit(); - }; - - handleNext = (values) => { - var name = values.fullName; - var dateString = values.year+'-'+values.month+'-'+zeroPad(values.day); - var { email, mrn } = values; - if(sundial.isValidDateForMask(dateString, 'YYYY-MM-DD')){ - var profile = { - fullName: name, - patient: { - birthday: dateString - } - }; - - if(email){ - profile.patient.email = email; - profile.emails = [email]; - } - - if(mrn){ - profile.patient.mrn = mrn; - } - - if(this.props.targetId){ - this.props.updateUser(profile); - } else { - this.props.createUser(profile); - } - } - }; - - renderCreateError = () => { - if (this.props.createCustodialAccountErrorDismissed || !this.props.createCustodialAccountErrorMessage) { - return null; - } - return ( -
- - {i18n.t(this.props.createCustodialAccountErrorMessage)} - -
- ); - }; - - renderUpdateError = () => { - if (this.props.updateProfileErrorDismissed || !this.props.updateProfileErrorMessage) { - return null; - } - return ( -
- - {i18n.t(this.props.updateProfileErrorMessage)} - -
- ); - }; - - renderDateInputs = (fields) => ( -
-
- - - -
- {this.renderDateError(fields)} -
- ); - - renderDateError = (fields) => { - const {month, day, year} = fields; - if (!year || !year.meta.error) { return null; } - // only render the error if each field has either been touched or has a value - // and the user is not interacting with any of them - const monthCheck = ((month.meta.touched || month.input.value) && !month.meta.active); - const dayCheck = ((day.meta.touched || day.input.value) && !day.meta.active); - const yearCheck = ((year.meta.touched || year.input.value) && !year.meta.active); - return monthCheck && dayCheck && yearCheck && - (
{year.meta.error}
); - }; - - render() { - const { handleSubmit, targetId, memberships } = this.props; - const isCustodialAccount = _.has(_.get(memberships, [targetId, 'permissions']), 'custodian'); - const titleText = targetId ? i18n.t('Edit patient account') : i18n.t('Create a new patient account'); - const editable = targetId ? isCustodialAccount : true; - - return ( -
-
-
- {titleText} -
-
- {_.get(this.props.allUsers, [this.props.loggedInUser, 'fullName'])} -
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- -
-
-
- {i18n.t('Cancel')} -
-
- {this.renderCreateError()} - {this.renderUpdateError()} -
-
-
- ); - } -} - -const ClinicUserEditWrapped = reduxForm({ - form: 'userEdit', - validate: validateForm -})(ClinicUserEdit); - -function mapStateToProps(state){ - let initialValues = {}; - - if(state.uploadTargetUser){ - var user = _.get(state.allUsers, state.uploadTargetUser); - initialValues = { - initialValues: { - fullName: personUtils.patientFullName(user), - year: _.get(user, ['patient', 'birthday'], '').substr(0,4), - month: _.get(user, ['patient', 'birthday'], '').substr(5,2), - day: _.get(user, ['patient', 'birthday'], '').substr(8,2), - email: _.get(user, ['patient', 'email'], ''), - mrn: _.get(user, ['patient', 'mrn'], '') - } - }; - }; - - return initialValues; -} - -export default connect(mapStateToProps)(ClinicUserEditWrapped); diff --git a/app/components/ClinicUserSelect.js b/app/components/ClinicUserSelect.js deleted file mode 100644 index 47a1d31d4..000000000 --- a/app/components/ClinicUserSelect.js +++ /dev/null @@ -1,166 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2014, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ -import Select from 'react-select'; - -var _ = require('lodash'); -var React = require('react'); -var PropTypes = require('prop-types'); -var sundial = require('sundial'); -var cx = require('classnames'); -var personUtils = require('../../lib/core/personUtils'); -var metrics = require('../constants/metrics'); - -var styles = require('../../styles/components/ClinicUserSelect.module.less'); - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -class ClinicUserSelect extends React.Component { - static propTypes = { - allUsers: PropTypes.object.isRequired, - onUserChange: PropTypes.func.isRequired, - targetId: PropTypes.string, - targetUsersForUpload: PropTypes.array.isRequired, - onAddUserClick: PropTypes.func.isRequired, - setTargetUser: PropTypes.func.isRequired - }; - - handleClickNext = (e) => { - e.preventDefault(); - if(this.props.targetId){ - this.props.onUserChange(this.props.targetId); - } - }; - - handleOnChange = (userId) => { - this.props.setTargetUser(userId, {eventName: metrics.CLINIC_SEARCH_SELECTED}); - }; - - valueRenderer = (option) => { - var user = _.get(this.props.allUsers, option.value); - var name = personUtils.patientFullName(user); - var bday = _.get(user, ['patient', 'birthday'], ''); - var mrn = _.get(user, ['patient', 'mrn'], ''); - - var formattedBday; - if (bday) { - formattedBday = sundial.translateMask(bday, 'YYYY-MM-DD', 'M/D/YYYY'); - } - - var formattedMrn; - if (mrn) { - formattedMrn = 'MRN:'+mrn; - } - - return ( -
-
- {name} {formattedMrn} -
-
- {formattedBday} -
-
- ); - }; - - renderSelector = () => { - var {allUsers} = this.props; - var targets = this.props.targetUsersForUpload; - var sorted = _.sortBy(targets, function(targetId) { - return personUtils.patientFullName(allUsers[targetId]); - }); - - var selectorOpts = _.map(sorted, function(targetId) { - var targetInfo = allUsers[targetId]; - var mrn = _.get(targetInfo, ['patient', 'mrn'], ''); - var bday = _.get(targetInfo, ['patient', 'birthday'], ''); - if(bday){ - bday = ' ' + sundial.translateMask(bday, 'YYYY-MM-DD', 'M/D/YYYY'); - } - if (mrn) { - mrn = ' ' + mrn; - } - var fullName = personUtils.patientFullName(targetInfo); - return {value: targetId, label: fullName + mrn + bday}; - }); - - return ( - - - - - ); - }); - - var carelink = _.remove(items, {'key': 'carelink'}); - - // TODO: when this gets the ES6 treatment, use computed property syntax - var formClassesObject = {}; - formClassesObject[styles.form] = true; - formClassesObject[styles.onlyme] = !this.props.userDropdownShowing; - formClassesObject[styles.groups] = this.props.userDropdownShowing; - formClassesObject[styles.clinic] = this.props.isClinicAccount; - var formClasses = cx(formClassesObject); - - var disabled = (this.props.targetDevices.length > 0 && - this.props.userIsSelected) && - !this.props.disabled ? - false : true; - return ( -
-
-

{i18n.t('Choose devices')}

-
{items}
-
-
- -
-
- ); - } - - handleSubmit = () => { - this.props.onDone(); - }; -} - -module.exports = DeviceSelection; diff --git a/app/components/DeviceTimeModal.js b/app/components/DeviceTimeModal.js deleted file mode 100644 index 99dbbaa71..000000000 --- a/app/components/DeviceTimeModal.js +++ /dev/null @@ -1,189 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2014-2016, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ - -import _ from 'lodash'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import sundial from 'sundial'; - -import { sync as syncActions } from '../actions/'; - -import styles from '../../styles/components/DeviceTimeModal.module.less'; - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -export class DeviceTimeModal extends Component { - determineDeviceType = () => { - const { showingDeviceTimePrompt } = this.props; - const { tags } = showingDeviceTimePrompt.cfg.deviceInfo; - if(_.indexOf(tags, 'insulin-pump') !== -1){ - return { value: 'insulin-pump', text: i18n.t('pump') }; - } - if(_.indexOf(tags, 'cgm') !== -1){ - return { value: 'cgm', text: i18n.t('CGM') }; - } - if(_.indexOf(tags, 'bgm') !== -1){ - return { value: 'bgm', text: i18n.t('meter') }; - } - return 'unknown'; - } - - isDevice = (name) => { - const { showingDeviceTimePrompt } = this.props; - const {deviceInfo} = showingDeviceTimePrompt.cfg; - return deviceInfo && deviceInfo.driverId && deviceInfo.driverId === name; - } - - handleContinue = () => { - const { sync, showingDeviceTimePrompt } = this.props; - showingDeviceTimePrompt.callback('updateTime'); - sync.dismissedDeviceTimePrompt(); - } - - handleCancel = () => { - const { sync, showingDeviceTimePrompt } = this.props; - showingDeviceTimePrompt.callback('deviceTimePromptClose'); - sync.dismissedDeviceTimePrompt(); - } - - getActions = () => { - const { showingDeviceTimePrompt: { cfg: { timezone }, times: { serverTime, deviceTime } } } = this.props; - const type = this.determineDeviceType(); - const reminder = this.getReminder(); - const buttons = []; - const footnote = type.value === 'bgm' ? '*' : ''; - if ( !this.isDevice('Animas') && - !this.isDevice('InsuletOmniPod') && - !this.isDevice('Medtronic') && // these two lines should be removed - !this.isDevice('Medtronic600') && // when we can update time on Medtronic pumps - !this.isDevice('Tandem') && - !this.isDevice('TrueMetrix') - ) { - buttons.push( -
- {i18n.t('Is the time on your {{text}} incorrect?', { text: type.text })}
  - -
- ); - } - buttons.push( -
- {i18n.t('Are you in {{timezone}}? Double-check',{ timezone: timezone })}
- {i18n.t('selected time zone and current device time.')} - {reminder} - -
- ); - - return buttons; - } - - getMessage = () => { - const type = this.determineDeviceType(); - const { showingDeviceTimePrompt: { cfg: { timezone } } } = this.props; - let message; - if (type.value === 'bgm') { - message = ( -
-
- {i18n.t('* Changing your device time will not change any previous records.')}
- {i18n.t('All future readings will be in {{timezone}}.', { timezone: timezone })} - {i18n.t('Click to learn more about meters and device time.')} -
-
- ); - } - return message; - } - - getReminder = () => { - const { showingDeviceTimePrompt: { cfg: { deviceInfo } } } = this.props; - let reminder; - if (deviceInfo.model === 'Dash') { - reminder = ( -
-
- {i18n.t('Remember to tap "Export" on the PDM before clicking "Upload".')} -
-
- ); - } - return reminder; - }; - - render() { - const { showingDeviceTimePrompt } = this.props; - - if(!showingDeviceTimePrompt){ - return null; - } - - const { showingDeviceTimePrompt: { cfg: { timezone }, times: { serverTime, deviceTime } } } = this.props; - - const type = this.determineDeviceType(); - const actions = this.getActions(); - const message = this.getMessage(); - - return ( -
-
-
-
{i18n.t('Your {{text}} doesn\'t appear to be in',{ text: type.text })}
-
{`${timezone}:`}
-
-
-
-
-
{timezone}:
-
{sundial.formatInTimezone(serverTime, timezone, 'LT, LL')}
-
-
-
{i18n.t('Device time:')}
-
{sundial.formatInTimezone(deviceTime, timezone, 'LT, LL')}
-
-
-
-
- {actions} -
- {message} -
-
- ); - } -}; - -export default connect( - (state, ownProps) => { - return { - showingDeviceTimePrompt: state.showingDeviceTimePrompt - }; - }, - (dispatch) => { - return { - sync: bindActionCreators(syncActions, dispatch) - }; - } -)(DeviceTimeModal); diff --git a/app/components/Footer.js b/app/components/Footer.js deleted file mode 100644 index cf47b701e..000000000 --- a/app/components/Footer.js +++ /dev/null @@ -1,71 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2016, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ - -import PropTypes from 'prop-types'; - -import React, { Component } from 'react'; - -import styles from '../../styles/components/Footer.module.less'; -import logo from '../../images/JDRF_Reverse_Logo x2.png'; -import debugMode from '../utils/debugMode'; -import env from '../utils/env'; - -let os, osName; -if(env.electron){ - os = require('os'); - osName = require('os-name'); -} - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -export default class Footer extends Component { - static propTypes = { - version: PropTypes.string.isRequired, - }; - - render() { - const {version} = this.props; - let osArch = ''; - let environment = ''; - - if (debugMode.isDebug) { - env.electron ? osArch = ` (${osName()} - ${os.arch()})`:''; - environment = ` - ${this.props.environment}`; - } - - return ( -
-
-
- {i18n.t('Get Support')} -
-
- {i18n.t('Privacy and Terms of Use')} -
-
- {i18n.t('Made possible by')} -
-
-
-
{`v${version}${osArch}${environment}`}
-
-
- ); - } -} diff --git a/app/components/Header.js b/app/components/Header.js deleted file mode 100644 index 015b90836..000000000 --- a/app/components/Header.js +++ /dev/null @@ -1,132 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2016, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ - -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import actions from '../actions/'; - -const asyncActions = actions.async; -const syncActions = actions.sync; - -import LoggedInAs from '../components/LoggedInAs'; - -import * as actionSources from '../constants/actionSources'; -import { pages, pagesMap } from '../constants/otherConstants'; - -import styles from '../../styles/components/Header.module.less'; -import logo from '../../images/Tidepool_Logo_Light x2.png'; - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -export class Header extends Component { - static propTypes = { - location: PropTypes.object.isRequired, - blipUrls: PropTypes.object.isRequired, - dropdown: PropTypes.bool.isRequired, - uploadIsInProgress: PropTypes.bool.isRequired, - user: PropTypes.object, - isClinicAccount: PropTypes.bool, - targetUsersForUpload: PropTypes.array - }; - - handleClickChooseDevices = metric => { - const { toggleDropdown } = this.props.sync; - const { setPage } = this.props.async; - // ensure dropdown closes after click - setPage(pages.SETTINGS, true, metric); - toggleDropdown(true, actionSources.UNDER_THE_HOOD); - }; - - handleCheckForUpdates = () => { - const { toggleDropdown } = this.props.sync; - toggleDropdown(true, actionSources.UNDER_THE_HOOD); - }; - - render() { - const { allUsers, dropdown, location } = this.props; - if (location.pathname === pagesMap.LOADING) { - return null; - } - - if (location.pathname === pagesMap.LOGIN) { - return ( -
-
- - {i18n.t('Sign up')} -
-
- -
-
- {i18n.t('Uploader')} -
-
- ); - } - - return ( -
-
-
- -
- -
-
- ); - } -} - -export default connect( - (state, ownProps) => { - function isClinicAccount(state) { - return _.indexOf(_.get(_.get(state.allUsers, state.loggedInUser, {}), 'roles', []), 'clinic') !== -1; - } - return { - // plain state - allUsers: state.allUsers, - blipUrls: state.blipUrls, - dropdown: state.dropdown, - loggedInUser: state.loggedInUser, - targetUsersForUpload: state.targetUsersForUpload, - uploadIsInProgress: state.working.uploading, - // derived state - isClinicAccount: isClinicAccount(state) - }; - }, - (dispatch) => { - return { - async: bindActionCreators(asyncActions, dispatch), - sync: bindActionCreators(syncActions, dispatch) - }; - } -)(Header); diff --git a/app/components/Loading.js b/app/components/Loading.js deleted file mode 100644 index 280d7c8d2..000000000 --- a/app/components/Loading.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2014, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -var React = require('react'); -var styles = require('../../styles/components/App.module.less'); -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -class Loading extends React.Component { - render() { - return
-
- {i18n.t('Loading...')} -
-
; - } -} - -module.exports = Loading; diff --git a/app/components/LoadingBar.js b/app/components/LoadingBar.js deleted file mode 100644 index 39aba50bc..000000000 --- a/app/components/LoadingBar.js +++ /dev/null @@ -1,32 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2014, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ - -var React = require('react'); - -var styles = require('../../styles/components/LoadingBar.module.less'); - -class LoadingBar extends React.Component { - render() { - return ( -
-
 
-
- ); - } -} - -module.exports = LoadingBar; diff --git a/app/components/LoggedInAs.js b/app/components/LoggedInAs.js deleted file mode 100644 index 69e0d657d..000000000 --- a/app/components/LoggedInAs.js +++ /dev/null @@ -1,180 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2014, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import env from '../utils/env'; - -let ipcRenderer; -if(env.electron_renderer){ - ({ipcRenderer} = require('electron')); -} -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -import styles from '../../styles/components/LoggedInAs.module.less'; - -export default class LoggedInAs extends Component { - static propTypes = { - dropMenu: PropTypes.bool.isRequired, - isUploadInProgress: PropTypes.bool.isRequired, - onCheckForUpdates: PropTypes.func.isRequired, - onChooseDevices: PropTypes.func.isRequired, - onClicked: PropTypes.func.isRequired, - onLogout: PropTypes.func.isRequired, - user: PropTypes.object, - isClinicAccount: PropTypes.bool, - targetUsersForUpload: PropTypes.array - } - - constructor(props) { - super(props); - this.state = { loggingOut: false }; - } - - noopHandler(e) { - if (e) { - e.preventDefault(); - } - } - - handleChooseDevices = e => { - e.preventDefault(); - this.props.onChooseDevices(); - }; - - handleCheckForUpdates = e => { - e.preventDefault(); - this.props.onCheckForUpdates(); - if(env.electron_renderer){ - ipcRenderer.send('autoUpdater','checkForUpdates'); - } - }; - - handleLogout = e => { - e.preventDefault(); - this.setState({ - loggingOut: true - }); - var self = this; - this.props.onLogout(function(err) { - if (err) { - self.setState({ - loggingOut: false - }); - } - }); - }; - - renderChooseDevices() { - var title = ''; - var uploadInProgress = this.props.isUploadInProgress; - var isDisabled = uploadInProgress; - - if (this.props.isClinicAccount) { - return null; - } - - if (_.isEmpty(this.props.targetUsersForUpload)) { - isDisabled = true; - } - - - if (uploadInProgress) { - title = i18n.t('Upload in progress!\nPlease wait to change device selection.'); - } else if (isDisabled) { - title = i18n.t('Set up data storage to upload devices.'); - } - - return ( -
  • - - - {i18n.t('Choose Devices')} - -
  • - ); - } - - renderCheckForUpdates() { - return ( -
  • - - - {i18n.t('Check for Updates')} - -
  • - ); - } - - renderLogout() { - var uploadInProgress = this.props.isUploadInProgress; - - if (this.state.loggingOut) { - return Logging out...; - } - - return ( - - - {i18n.t('Logout')} - - ); - } - - renderDropMenu() { - function stopPropagation(e) { - e.stopPropagation(); - } - return ( -
    - -
    - ); - } - - render() { - var dropMenu = this.props.dropMenu ? this.renderDropMenu() : null; - var {user} = this.props; - - return ( -
    -
    - {_.get(user, 'fullName', '')} - -
    - {dropMenu} -
    - ); - } -} diff --git a/app/components/Login.js b/app/components/Login.js deleted file mode 100644 index d8eb8b78e..000000000 --- a/app/components/Login.js +++ /dev/null @@ -1,127 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2014, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import styles from '../../styles/components/Login.module.less'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import actions from '../actions/'; -const asyncActions = actions.async; - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -export class Login extends Component { - renderForgotPasswordLink() { - return ( - - {i18n.t('Forgot password?')} - - ); - } - - renderButton() { - var text = i18n.t('Log in'); - - if (this.props.isFetching) { - text = i18n.t('Logging in...'); - } - - return ( - - ); - } - - handleLogin(e) { - e.preventDefault(); - var username = this.username.value; - var password = this.password.value; - var remember = this.remember.checked; - - this.props.onLogin( - {username: username, password: password}, - {remember: remember} - ); - } - - renderError() { - if (!this.props.errorMessage) { - return null; - } - - return {i18n.t(this.props.errorMessage)}; - } - - render() { - return ( -
    -
    -
    - { this.username = input; }} placeholder={i18n.t('Email')}/> -
    -
    - { this.password = input; }} placeholder={i18n.t('Password')} type="password"/> -
    -
    -
    -
    - { this.remember = input; }} id="remember"/> - -
    -
    {this.renderForgotPasswordLink()}
    -
    -
    - {this.renderButton()} -
    -
    -
    {this.renderError()}
    -
    -
    - ); - } -} - -Login.propTypes = { - disabled: PropTypes.bool.isRequired, - errorMessage: PropTypes.string, - forgotPasswordUrl: PropTypes.string.isRequired, - isFetching: PropTypes.bool.isRequired, - onLogin: PropTypes.func.isRequired -}; - -export default connect( - (state) => { - return { - disabled: Boolean(state.unsupported), - errorMessage: state.loginErrorMessage, - forgotPasswordUrl: state.blipUrls.forgotPassword, - isFetching: state.working.fetchingUserInfo, - }; - }, - (dispatch) => { - return { - onLogin: bindActionCreators(asyncActions.doLogin, dispatch) - }; - } -)(Login); diff --git a/app/components/NoUploadTargets.js b/app/components/NoUploadTargets.js deleted file mode 100644 index f9a598e8b..000000000 --- a/app/components/NoUploadTargets.js +++ /dev/null @@ -1,52 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2016, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ - -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; - -import { urls } from '../constants/otherConstants'; - -import styles from '../../styles/components/NoUploadTargets.module.less'; - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -export default class NoUploadTargets extends Component { - static propTypes = { - newPatientLink: PropTypes.string.isRequired, - }; - - render() { - const { newPatientLink } = this.props; - - return ( -
    -
    - -
    - {i18n.t('Set up data storage')} -
    -
    -

    {i18n.t('Or, ask the person you are uploading for to grant you access to upload.')}
    {i18n.t('How?')}

    -
    -
    - ); - } -} diff --git a/app/components/ProgressBar.js b/app/components/ProgressBar.js deleted file mode 100644 index 1df3f4a7b..000000000 --- a/app/components/ProgressBar.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2014, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ - -var React = require('react'); -var PropTypes = require('prop-types'); - -var styles = require('../../styles/components/ProgressBar.module.less'); - -class ProgressBar extends React.Component { - static propTypes = { - // Percentage is an integer between 0 and 100 - percentage: PropTypes.number.isRequired - }; - - render() { - // Minimum fill of 1% - var width = this.props.percentage ? this.props.percentage : 1; - return ( -
    -
     
    -
    - ); - } -} - -module.exports = ProgressBar; diff --git a/app/components/TimezoneDropdown.js b/app/components/TimezoneDropdown.js deleted file mode 100644 index ffd5df9f5..000000000 --- a/app/components/TimezoneDropdown.js +++ /dev/null @@ -1,174 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2014, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ -import Select from 'react-select'; - -var _ = require('lodash'); -var React = require('react'); -var PropTypes = require('prop-types'); -var sundial = require('sundial'); -var cx = require('classnames'); - -var styles = require('../../styles/components/TimezoneDropdown.module.less'); - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -class TimezoneDropdown extends React.Component { - constructor(props) { - super(props); - - this.timezoneSelect = null; - - this.setTimezoneSelect = element => { - this.timezoneSelect = element; - }; - } - - static propTypes = { - onTimezoneChange: PropTypes.func.isRequired, - selectorLabel: PropTypes.string.isRequired, - // targetId can be null when logged in user is not a data storage account - // for example a clinic worker - targetId: PropTypes.string, - targetTimezone: PropTypes.string, - updateProfileErrorMessage: PropTypes.string, - updateProfileErrorDismissed: PropTypes.bool, - dismissUpdateProfileError: PropTypes.func.isRequired, - isClinicAccount: PropTypes.bool, - userDropdownShowing: PropTypes.bool, - isUploadInProgress: PropTypes.bool.isRequired, - onBlur: PropTypes.func.isRequired, - isTimezoneFocused: PropTypes.bool.isRequired - }; - - UNSAFE_componentWillReceiveProps(nextProps) { - if (!this.props.targetId && nextProps.targetId !== null) { - if (this.props.targetTimezone !== null) { - this.props.onTimezoneChange( - nextProps.targetId, - this.props.targetTimezone - ); - } - } - } - - componentDidMount() { - var self = this; - self.updateSuggestedInterval = setInterval( - function(){ - self.setState({time: new Date()}); - }, 1000 * 60 - ); - } - - componentWillUnmount() { - clearInterval(this.updateSuggestedInterval); - } - - componentDidUpdate() { - if (this.timezoneSelect && this.props.isTimezoneFocused) { - this.timezoneSelect.focus(); - } - } - - buildTzSelector = () => { - function sortByOffset(timezones) { - return _.sortBy(timezones, function(tz) { - return tz.offset; - }); - } - var timezones = sundial.getTimezones(); - var opts = sortByOffset(timezones.bigFour) - .concat(sortByOffset(timezones.unitedStates)) - .concat(sortByOffset(timezones.hoisted)) - .concat(sortByOffset(timezones.theRest)); - var targetUser = this.props.targetId || 'noUserSelected'; - - return ( - - - ); - } - - renderButton() { - const { text, upload } = this.props; - let labelText = text.LABEL_UPLOAD; - let disabled = upload.disabled || this.props.disabled; - - if (_.get(upload, 'source.type', null) === 'carelink') { - labelText = 'Enable'; - disabled = false; - } - - if (_.get(upload, 'key', null) === 'medtronic') { - disabled = disabled || this.state.medtronicFormIncomplete; - } - - if (_.get(upload, 'key', null) === 'medtronic600') { - disabled = disabled || this.state.medtronic600FormIncomplete; - } - - if (_.get(upload, 'source.type', null) === 'block') { - return null; - } - - return ( -
    - -
    - ); - } - - renderCareLinkInputs() { - const { upload } = this.props; - if (_.get(upload, 'source.type', null) !== 'carelink') { - return null; - } - - return ( -
    -
    - {i18n.t('Medtronic has removed the CareLink export feature.')} - {i18n.t('Click below to enable direct upload to Tidepool using a Contour Next Link.')} -
    -
    - ); - } - - renderMedtronicSerialNumberInput() { - const { upload } = this.props; - if (_.get(upload, 'source.driverId', null) !== 'Medtronic') { - return null; - } - - return ( -
    -
    -

    {i18n.t('Enter your 6 digit serial number found on the back of your pump.')}

    - -
    - - -
    -
    -
    - ); - } - - renderMedtronic600SerialNumberInput() { - const { upload } = this.props; - if (_.get(upload, 'source.driverId', null) !== 'Medtronic600') { - return null; - } - - const divHidden = cx({ - [styles.hidden]: this.state.medtronic600Linked, - }); - - const serialInputStyle = cx({ - [styles.textInput]: this.state.medtronic600SerialNumberValid, - [styles.textInputError]: !this.state.medtronic600SerialNumberValid, - }); - - return ( -
    -
    -
    - - -
    -
    -

    {i18n.t('Enter 10 character serial number.')}

    - -
    -
    -
    - ); - } - - renderMedtronicUploadRangeSelect() { - const { upload } = this.props; - if (_.get(upload, 'source.driverId', null) !== 'Medtronic600') { - return null; - } - const opts = [ - { label: i18n.t('since last upload'), value: uploadDataPeriod.PERIODS.DELTA }, - { label: i18n.t('last 4 weeks'), value: uploadDataPeriod.PERIODS.FOUR_WEEKS }, - { label: i18n.t('all data on pump'), value: uploadDataPeriod.PERIODS.ALL } - ]; - return ( -
    -
    Upload:
    -
    - - ); - }; - - render() { - // we're already doing a check to see if we want to render in App.js - // but this is an extra measure of protection against trying to render - // when we don't have the potential target users to do so - if (_.isEmpty(this.props.targetUsersForUpload)) { - return null; - } - - var text = this.props.locationPath === pagesMap.MAIN ? - i18n.t('Upload data for') : i18n.t('Choose devices for'); - var styleClass = this.props.locationPath.substring(1); - - return ( -
    -
    -
    {text}
    -
    - {this.groupSelector()} -
    -
    -
    - ); - } -} - -module.exports = UserDropdown; diff --git a/app/components/VersionCheckError.js b/app/components/VersionCheckError.js deleted file mode 100644 index ecfed7769..000000000 --- a/app/components/VersionCheckError.js +++ /dev/null @@ -1,87 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2016, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ - -import cx from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; - -import errorText from '../constants/errors'; - -import styles from '../../styles/components/VersionCheck.module.less'; -import CloudOff from '@material-ui/icons/CloudOff'; - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -export default class VersionCheckError extends Component { - static propTypes = { - errorMessage: PropTypes.string.isRequired, - errorText: PropTypes.object.isRequired - }; - - static defaultProps = { - errorText: { - CONNECT: i18n.t('Please check your connection, quit & relaunch to try again.'), - ERROR_DETAILS: i18n.t('Details for Tidepool\'s developers:'), - OFFLINE: i18n.t('You\'re not connected to the Internet.'), - SERVERS_DOWN: i18n.t('We can\'t connect to Tidepool right now.'), - TRY_AGAIN: i18n.t('Quit & relaunch the Uploader to try again.') - } - }; - - constructor(props) { - super(props); - } - - render() { - const { errorMessage } = this.props; - const userErrorText = this.props.errorText; - const offline = errorMessage === errorText.E_OFFLINE; - const errorDetails = offline ? null : ( -
    -

    {userErrorText.ERROR_DETAILS}

    -

    {errorMessage}

    -
    - ); - const firstLine = offline - ? userErrorText.OFFLINE - : userErrorText.SERVERS_DOWN; - const secondLine = offline - ? userErrorText.CONNECT - : userErrorText.TRY_AGAIN; - const versionCheckClass = cx({ - [styles.failed]: !offline, - [styles.offline]: offline - }); - - return ( -
    -
    -
    -
    - -

    {firstLine}

    -

    {secondLine}

    -
    - {errorDetails} -
    -
    -
    - ); - } -} diff --git a/app/components/ViewDataLink.js b/app/components/ViewDataLink.js deleted file mode 100644 index 7a43ee5df..000000000 --- a/app/components/ViewDataLink.js +++ /dev/null @@ -1,49 +0,0 @@ -/* -* == BSD2 LICENSE == -* Copyright (c) 2014, Tidepool Project -* -* This program is free software; you can redistribute it and/or modify it under -* the terms of the associated License, which is identical to the BSD 2-Clause -* License as published by the Open Source Initiative at opensource.org. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the License for more details. -* -* You should have received a copy of the License along with this program; if -* not, you can obtain one from Tidepool Project at tidepool.org. -* == BSD2 LICENSE == -*/ - -var _ = require('lodash'); -var PropTypes = require('prop-types'); -var React = require('react'); - -var styles = require('../../styles/components/ViewDataLink.module.less'); - -//const remote = require('@electron/remote'); -// const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; - -class ViewDataLink extends React.Component { - static propTypes = { - href: PropTypes.string.isRequired, - onViewClicked: PropTypes.func.isRequired - }; - - render() { - return ( -
    - - {i18n.t('See data')} - -
    - ); - } -} - -module.exports = ViewDataLink; diff --git a/app/constants/actionSources.js b/app/constants/actionSources.js deleted file mode 100644 index 54b571e2e..000000000 --- a/app/constants/actionSources.js +++ /dev/null @@ -1,124 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2015, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -export const USER = 'USER'; -export const USER_VISIBLE = 'USER_VISIBLE'; -export const UNDER_THE_HOOD = 'UNDER_THE_HOOD'; - -/** - * Syncronous action types - */ -export const ADD_TARGET_DEVICE = USER; -export const CLICK_GO_TO_BLIP = USER; -export const CLINIC_ADD_MRN = USER; -export const CLINIC_ADD_EMAIL = USER; -export const CLINIC_DEVICE_STORED = USER; -export const CLINIC_ADD_INVALID_DATE = USER; -export const HIDE_UNAVAILABLE_DEVICES = USER_VISIBLE; -export const REMOVE_TARGET_DEVICE = USER; -export const RESET_UPLOAD = USER; -export const RETRIEVING_USERS_TARGETS = UNDER_THE_HOOD; -export const SET_BLIP_VIEW_DATA_URL = USER_VISIBLE; -export const SET_DEFAULT_TARGET_ID = USER_VISIBLE; -export const SET_FORGOT_PASSWORD_URL = USER_VISIBLE; -export const SET_NEW_PATIENT_URL = USER_VISIBLE; -export const SET_OS = UNDER_THE_HOOD; -export const SET_PAGE = USER_VISIBLE; -export const SET_SIGNUP_URL = USER_VISIBLE; -export const SET_TARGET_TIMEZONE = USER; -export const SET_UPLOADS = UNDER_THE_HOOD; -export const SET_UPLOAD_TARGET_USER = USER; -export const SET_USER_INFO_FROM_TOKEN = USER_VISIBLE; -export const SET_USERS_TARGETS = USER_VISIBLE; -export const SET_VERSION = USER_VISIBLE; -export const STORING_USERS_TARGETS = UNDER_THE_HOOD; -export const TOGGLE_DROPDOWN = USER; -export const TOGGLE_ERROR_DETAILS = USER; -export const DISMISS_UPDATE_PROFILE_ERROR = USER; -export const DISMISS_CREATE_CUSTODIAL_ACCOUNT_ERROR = USER; -export const SET_ALL_USERS = UNDER_THE_HOOD; -export const TIMEZONE_BLUR = UNDER_THE_HOOD; - -/* - * Asyncronous action types - */ - -export const INIT_APP_REQUEST = UNDER_THE_HOOD; -export const INIT_APP_SUCCESS = UNDER_THE_HOOD; -export const INIT_APP_FAILURE = USER_VISIBLE; - -// user.login -export const LOGIN_REQUEST = USER; -export const LOGIN_SUCCESS = USER_VISIBLE; -export const LOGIN_FAILURE = USER_VISIBLE; - -// user.logout -export const LOGOUT_REQUEST = USER; -export const LOGOUT_SUCCESS = USER_VISIBLE; -// because we don't surface logout errors in the UI -export const LOGOUT_FAILURE = UNDER_THE_HOOD; - -// uploading devices -export const UPLOAD_REQUEST = USER; -export const UPLOAD_PROGRESS = USER_VISIBLE; -export const UPLOAD_SUCCESS = USER_VISIBLE; -export const UPLOAD_FAILURE = USER_VISIBLE; -export const UPLOAD_ABORTED = USER_VISIBLE; -export const UPLOAD_CANCELLED = USER_VISIBLE; - -export const CARELINK_FETCH_REQUEST = USER; -export const CARELINK_FETCH_SUCCESS = USER_VISIBLE; -export const CARELINK_FETCH_FAILURE = USER_VISIBLE; - -export const CARELINK_UPLOAD_REQUEST = UNDER_THE_HOOD; -export const CARELINK_UPLOAD_SUCCESS = USER_VISIBLE; -export const CARELINK_UPLOAD_FAILURE = USER_VISIBLE; - -export const DEVICE_DETECT_REQUEST = UNDER_THE_HOOD; -export const DEVICE_DETECT_FAILURE = USER_VISIBLE; -export const DEVICE_DETECT_SUCCESS = UNDER_THE_HOOD; - -export const DEVICE_TIME_INCORRECT = USER_VISIBLE; -export const DISMISS_DEVICE_TIME_PROMPT = USER_VISIBLE; - -export const READ_FILE_REQUEST = USER; -export const READ_FILE_SUCCESS = USER_VISIBLE; -export const READ_FILE_FAILURE = USER_VISIBLE; -export const READ_FILE_ABORTED = USER_VISIBLE; -export const CHOOSING_FILE = USER; - -// version check -export const VERSION_CHECK_REQUEST = UNDER_THE_HOOD; -export const VERSION_CHECK_SUCCESS = UNDER_THE_HOOD; -export const VERSION_CHECK_FAILURE = USER_VISIBLE; - -// update profile -export const UPDATE_PROFILE_REQUEST = UNDER_THE_HOOD; -export const UPDATE_PROFILE_SUCCESS = UNDER_THE_HOOD; -export const UPDATE_PROFILE_FAILURE = USER_VISIBLE; - -// create custodial account -export const CREATE_CUSTODIAL_ACCOUNT_REQUEST = UNDER_THE_HOOD; -export const CREATE_CUSTODIAL_ACCOUNT_SUCCESS = UNDER_THE_HOOD; -export const CREATE_CUSTODIAL_ACCOUNT_FAILURE = USER_VISIBLE; - -// application update -export const QUIT_AND_INSTALL = UNDER_THE_HOOD; - -// ad hoc pairing -export const AD_HOC_PAIRING_REQUEST = USER_VISIBLE; -export const AD_HOC_PAIRING_DISMISSED = USER; diff --git a/app/constants/actionTypes.js b/app/constants/actionTypes.js deleted file mode 100644 index 58a890bc2..000000000 --- a/app/constants/actionTypes.js +++ /dev/null @@ -1,138 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2015, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -/** - * Syncronous action types - */ -export const ADD_TARGET_DEVICE = 'ADD_TARGET_DEVICE'; -export const CLICK_GO_TO_BLIP = 'CLICK_GO_TO_BLIP'; -export const CLINIC_ADD_MRN = 'CLINIC_ADD_MRN'; -export const CLINIC_ADD_EMAIL = 'CLINIC_ADD_EMAIL'; -export const CLINIC_DEVICE_STORED = 'CLINIC_DEVICE_STORED'; -export const CLINIC_ADD_INVALID_DATE = 'CLINIC_ADD_INVALID_DATE'; -export const HIDE_UNAVAILABLE_DEVICES = 'HIDE_UNAVAILABLE_DEVICES'; -export const REMOVE_TARGET_DEVICE = 'REMOVE_TARGET_DEVICE'; -export const RESET_UPLOAD = 'RESET_UPLOAD'; -export const RETRIEVING_USERS_TARGETS = 'RETRIEVING_USERS_TARGETS'; -export const SET_BLIP_VIEW_DATA_URL = 'SET_BLIP_VIEW_DATA_URL'; -export const SET_DEFAULT_TARGET_ID = 'SET_DEFAULT_TARGET_ID'; -export const SET_FORGOT_PASSWORD_URL = 'SET_FORGOT_PASSWORD_URL'; -export const SET_NEW_PATIENT_URL = 'SET_NEW_PATIENT_URL'; -export const SET_OS = 'SET_OS'; -export const SET_PAGE = 'SET_PAGE'; -export const SET_SIGNUP_URL = 'SET_SIGNUP_URL'; -export const SET_TARGET_TIMEZONE = 'SET_TARGET_TIMEZONE'; -export const SET_UPLOADS = 'SET_UPLOADS'; -export const SET_UPLOAD_TARGET_USER = 'SET_UPLOAD_TARGET_USER'; -export const SET_USER_INFO_FROM_TOKEN = 'SET_USER_INFO_FROM_TOKEN'; -export const SET_USERS_TARGETS = 'SET_USERS_TARGETS'; -export const SET_VERSION = 'SET_VERSION'; -export const STORING_USERS_TARGETS = 'STORING_USERS_TARGETS'; -export const TOGGLE_DROPDOWN = 'TOGGLE_DROPDOWN'; -export const TOGGLE_ERROR_DETAILS = 'TOGGLE_ERROR_DETAILS'; -export const DISMISS_UPDATE_PROFILE_ERROR = 'DISMISS_UPDATE_PROFILE_ERROR'; -export const DISMISS_CREATE_CUSTODIAL_ACCOUNT_ERROR = 'DISMISS_CREATE_CUSTODIAL_ACCOUNT_ERROR'; -export const SET_ALL_USERS = 'SET_ALL_USERS'; -export const TIMEZONE_BLUR = 'TIMEZONE_BLUR'; - -/* - * Asyncronous action types - */ - -export const INIT_APP_REQUEST = 'INIT_APP_REQUEST'; -export const INIT_APP_SUCCESS = 'INIT_APP_SUCCESS'; -export const INIT_APP_FAILURE = 'INIT_APP_FAILURE'; - -// user.login -export const LOGIN_REQUEST = 'LOGIN_REQUEST'; -export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; -export const LOGIN_FAILURE = 'LOGIN_FAILURE'; - -// user.logout -export const LOGOUT_REQUEST = 'LOGOUT_REQUEST'; -export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; -export const LOGOUT_FAILURE = 'LOGIN_FAILURE'; - -// uploading devices -export const UPLOAD_REQUEST = 'UPLOAD_REQUEST'; -export const UPLOAD_PROGRESS = 'UPLOAD_PROGRESS'; -export const UPLOAD_SUCCESS = 'UPLOAD_SUCCESS'; -export const UPLOAD_FAILURE = 'UPLOAD_FAILURE'; -export const UPLOAD_ABORTED = 'UPLOAD_ABORTED'; -export const UPLOAD_CANCELLED = 'UPLOAD_CANCELLED'; - -export const CARELINK_FETCH_REQUEST = 'CARELINK_FETCH_REQUEST'; -export const CARELINK_FETCH_SUCCESS = 'CARELINK_FETCH_SUCCESS'; -export const CARELINK_FETCH_FAILURE = 'CARELINK_FETCH_FAILURE'; - -export const CARELINK_UPLOAD_REQUEST = 'CARELINK_UPLOAD_REQUEST'; -export const CARELINK_UPLOAD_SUCCESS = 'CARELINK_UPLOAD_SUCCESS'; -export const CARELINK_UPLOAD_FAILURE = 'CARELINK_UPLOAD_FAILURE'; - -export const MEDTRONIC_REMEMBER_SERIAL_NUMBER = 'MEDTRONIC_REMEMBER_SERIAL_NUMBER'; - -export const DEVICE_DETECT_REQUEST = 'DEVICE_DETECT_REQUEST'; -export const DEVICE_DETECT_SUCCESS = 'DEVICE_DETECT_SUCCESS'; -export const DEVICE_DETECT_FAILURE = 'DEVICE_DETECT_FAILURE'; - -export const READ_FILE_REQUEST = 'READ_FILE_REQUEST'; -export const READ_FILE_SUCCESS = 'READ_FILE_SUCCESS'; -export const READ_FILE_FAILURE = 'READ_FILE_FAILURE'; -export const READ_FILE_ABORTED = 'READ_FILE_ABORTED'; -export const CHOOSING_FILE = 'CHOOSING_FILE'; - -// version check -export const VERSION_CHECK_REQUEST = 'VERSION_CHECK_REQUEST'; -export const VERSION_CHECK_SUCCESS = 'VERSION_CHECK_SUCCESS'; -export const VERSION_CHECK_FAILURE = 'VERSION_CHECK_FAILURE'; - -// update profile -export const UPDATE_PROFILE_REQUEST = 'UPDATE_PROFILE_REQUEST'; -export const UPDATE_PROFILE_SUCCESS = 'UPDATE_PROFILE_SUCCESS'; -export const UPDATE_PROFILE_FAILURE = 'UPDATE_PROFILE_FAILURE'; - -// create custodial account -export const CREATE_CUSTODIAL_ACCOUNT_REQUEST = 'CREATE_CUSTODIAL_ACCOUNT_REQUEST'; -export const CREATE_CUSTODIAL_ACCOUNT_SUCCESS = 'CREATE_CUSTODIAL_ACCOUNT_SUCCESS'; -export const CREATE_CUSTODIAL_ACCOUNT_FAILURE = 'CREATE_CUSTODIAL_ACCOUNT_FAILURE'; - -// autoUpdater -export const CHECKING_FOR_UPDATES = 'CHECKING_FOR_UPDATES'; -export const UPDATE_AVAILABLE = 'UPDATE_AVAILABLE'; -export const UPDATE_NOT_AVAILABLE = 'UPDATE_NOT_AVAILABLE'; -export const AUTOUPDATE_ERROR = 'AUTOUPDATE_ERROR'; -export const UPDATE_DOWNLOADED = 'UPDATE_DOWNLOADED'; -export const DISMISS_UPDATE_AVAILABLE = 'DISMISS_UPDATE_AVAILABLE'; -export const DISMISS_UPDATE_NOT_AVAILABLE = 'DISMISS_UPDATE_NOT_AVAILABLE'; -export const AUTO_UPDATE_CHECKING_FOR_UPDATES = 'AUTO_UPDATE_CHECKING_FOR_UPDATES'; -export const MANUAL_UPDATE_CHECKING_FOR_UPDATES = 'MANUAL_UPDATE_CHECKING_FOR_UPDATES'; -export const QUIT_AND_INSTALL = 'QUIT_AND_INSTALL'; - -// driver update -export const CHECKING_FOR_DRIVER_UPDATE = 'CHECKING_FOR_DRIVER_UPDATE'; -export const DRIVER_UPDATE_AVAILABLE = 'DRIVER_UPDATE_AVAILABLE'; -export const DRIVER_UPDATE_NOT_AVAILABLE = 'DRIVER_UPDATE_NOT_AVAILABLE'; -export const DISMISS_DRIVER_UPDATE_AVAILABLE = 'DISMISS_DRIVER_UPDATE_AVAILABLE'; -export const DRIVER_INSTALL = 'DRIVER_INSTALL'; -export const DRIVER_INSTALL_SHELL_OPTS = 'DRIVER_INSTALL_SHELL_OPTS'; - -export const DEVICE_TIME_INCORRECT = 'DEVICE_TIME_INCORRECT'; -export const DISMISS_DEVICE_TIME_PROMPT = 'DISMISS_DEVICE_TIME_PROMPT'; - -// ad hoc pairing -export const AD_HOC_PAIRING_REQUEST = 'AD_HOC_PAIRING_REQUEST'; -export const AD_HOC_PAIRING_DISMISSED = 'AD_HOC_PAIRING_DISMISSED'; diff --git a/app/constants/errors.js b/app/constants/errors.js index 6bee4f003..72af61355 100644 --- a/app/constants/errors.js +++ b/app/constants/errors.js @@ -1,3 +1,4 @@ + /* * == BSD2 LICENSE == * Copyright (c) 2016, Tidepool Project diff --git a/app/constants/metrics.js b/app/constants/metrics.js deleted file mode 100644 index 3aa70cd3a..000000000 --- a/app/constants/metrics.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -export const CLICK_GO_TO_BLIP = 'Clicked See Data in Blip'; - -export const LOGIN_SUCCESS = 'Login Successful'; -export const LOGOUT_REQUEST = 'Clicked Log Out'; - -export const CLINIC_LOGIN_SUCCESS = 'VCA Login Successful'; -export const CLINIC_SEARCH_DISPLAYED = 'VCA Search Screen Displayed'; -export const CLINIC_SEARCH_SELECTED = 'VCA Search Patient Selected'; -export const CLINIC_NEXT = 'VCA Search Next'; -export const CLINIC_ADD = 'VCA Search Add New'; -export const CLINIC_ADD_CANCEL = 'VCA Add Canceled'; -export const CLINIC_ADD_MRN = 'VCA Add MRN Saved'; -export const CLINIC_ADD_EMAIL = 'VCA Add Patient Email Saved'; -export const CLINIC_ADD_NEW_PATIENT = 'VCA Add New Patient Saved'; -export const CLINIC_ADD_INVALID_DATE = 'VCA Add Invalid Date'; -export const CLINIC_CHANGE_PERSON = 'VCA Change Person'; -export const CLINIC_EDIT_INFO = 'VCA Edit Info'; -export const CLINIC_DEVICE_STORED = 'VCA Device Stored'; -export const CLINIC_DEVICES_DONE = 'VCA Devices Done'; -export const CLINIC_TIMEZONE_SELECT = 'VCA Timezone Selected'; -export const CLINIC_CHANGE_DEVICES = 'VCA Change Devices'; - -export const CHOOSE_DEVICES = 'Choose Devices'; -export const CHOOSE_DEVICES_DONE = 'Choose Devices Done'; - -export const UPLOAD_REQUEST = 'Upload Attempted'; -export const UPLOAD_SUCCESS = 'Upload Successful'; -export const UPLOAD_FAILURE = 'Upload Failed'; - -export const CARELINK_FETCH_SUCCESS = 'CareLink Fetch Successful'; -export const CARELINK_FETCH_FAILURE = 'CareLink Fetch Failed'; - -export const MEDTRONIC_REMEMBER_SERIAL_NUMBER = 'Medtronic Remember Serial Number'; - -export const VERSION_CHECK_FAILURE_OUTDATED = '(Partial) Uploader Version No Longer Supported'; - -export const UNSUPPORTED_SCREEN_DISPLAYED = 'Electron Uploader - Unsupported screen displayed'; -export const QUIT_AND_INSTALL = 'Electron Uploader - Auto-update occurred'; - -export const DEVICE_TIME_INCORRECT = 'Device time incorrect'; - diff --git a/app/constants/otherConstants.js b/app/constants/otherConstants.js deleted file mode 100644 index 70aacae44..000000000 --- a/app/constants/otherConstants.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2015, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -export const pages = { - LOADING: 'LOADING', - LOGIN: 'LOGIN', - MAIN: 'MAIN', - NO_UPLOAD_TARGETS: 'NO_UPLOAD_TARGETS', - SETTINGS: 'SETTINGS', - CLINIC_USER_SELECT: 'CLINIC_USER_SELECT', - CLINIC_USER_EDIT: 'CLINIC_USER_EDIT' -}; - -export const pagesMap = { - LOADING: '/', - LOGIN: '/login', - MAIN: '/main', - NO_UPLOAD_TARGETS: '/no_upload_targets', - SETTINGS: '/settings', - CLINIC_USER_SELECT: '/clinic_user_select', - CLINIC_USER_EDIT: '/clinic_user_edit' -}; - -export const paths = { - FORGOT_PASSWORD: '/request-password-from-uploader', - SIGNUP: '/signup', - NEW_PATIENT: '/patients/new' -}; - -export const steps = { - start: 'START', - carelinkFetch: 'CARELINK_FETCH', - choosingFile: 'CHOOSING_FILE', - detect: 'DETECT', - setup: 'SETUP', - connect: 'CONNECT', - getConfigInfo: 'GET_CONFIG_INFO', - fetchData: 'FETCH_DATA', - processData: 'PROCESS_DATA', - uploadData: 'UPLOAD_DATA', - disconnect: 'DISCONNECT', - cleanup: 'CLEANUP' -}; - -export const urls = { - HOW_TO_UPDATE_KB_ARTICLE: 'http://support.tidepool.org/article/6-how-to-install-or-upgrade-the-tidepool-uploader-gen', - HOW_TO_SHARE_DATA_KB_ARTICLE: 'http://support.tidepool.org/article/16-share-your-data' -}; diff --git a/app/containers/App.js b/app/containers/App.js deleted file mode 100644 index 80959115f..000000000 --- a/app/containers/App.js +++ /dev/null @@ -1,326 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2014-2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import * as metrics from '../constants/metrics'; -import { Route, Switch } from 'react-router-dom'; -import { hot } from 'react-hot-loader'; - -import bows from 'bows'; - -import config from '../../lib/config.js'; -import env from '../utils/env'; - -//import carelink from '../../lib/core/carelink.js'; -let carelink = {init: (a,b)=>b(null)}; -import device from '../../lib/core/device.js'; -import localStore from '../../lib/core/localStore.js'; - -import actions from '../actions/'; -const asyncActions = actions.async; -const syncActions = actions.sync; - -import * as actionSources from '../constants/actionSources'; -import { pages, urls, pagesMap } from '../constants/otherConstants'; -import debugMode from '../utils/debugMode'; - -import MainPage from './MainPage'; -import Login from '../components/Login'; -import Loading from '../components/Loading'; -import SettingsPage from './SettingsPage'; -import ClinicUserSelectPage from './ClinicUserSelectPage'; -import ClinicUserEditPage from './ClinicUserEditPage'; -import NoUploadTargetsPage from './NoUploadTargetsPage'; -import UpdatePlease from '../components/UpdatePlease'; -import VersionCheckError from '../components/VersionCheckError'; -import Footer from '../components/Footer'; -import Header from '../components/Header'; -import UpdateModal from '../components/UpdateModal'; -import UpdateDriverModal from '../components/UpdateDriverModal'; -import DeviceTimeModal from '../components/DeviceTimeModal'; -import AdHocModal from '../components/AdHocModal'; - -import styles from '../../styles/components/App.module.less'; - -let remote, dns, checkVersion; -if(env.electron_renderer){ - remote = require('@electron/remote'); - dns = require('dns'); - ({checkVersion} = require('../utils/drivers')); -} - -const serverdata = { - Local: { - API_URL: 'http://localhost:8009', - UPLOAD_URL: 'http://localhost:9122', - DATA_URL: 'http://localhost:9220', - BLIP_URL: 'http://localhost:3000' - }, - Development: { - API_URL: 'https://dev-api.tidepool.org', - UPLOAD_URL: 'https://dev-uploads.tidepool.org', - DATA_URL: 'https://dev-api.tidepool.org/dataservices', - BLIP_URL: 'https://dev-app.tidepool.org' - }, - Staging: { - API_URL: 'https://stg-api.tidepool.org', - UPLOAD_URL: 'https://stg-uploads.tidepool.org', - DATA_URL: 'https://stg-api.tidepool.org/dataservices', - BLIP_URL: 'https://stg-app.tidepool.org' - }, - Integration: { - API_URL: 'https://int-api.tidepool.org', - UPLOAD_URL: 'https://int-uploads.tidepool.org', - DATA_URL: 'https://int-api.tidepool.org/dataservices', - BLIP_URL: 'https://int-app.tidepool.org' - }, - Production: { - API_URL: 'https://api.tidepool.org', - UPLOAD_URL: 'https://uploads.tidepool.org', - DATA_URL: 'https://api.tidepool.org/dataservices', - BLIP_URL: 'https://app.tidepool.org' - }, - QA2: { - API_URL: 'https://qa2.development.tidepool.org', - UPLOAD_URL: 'https://int-uploads.tidepool.org', - DATA_URL: 'https://qa2.development.tidepool.org/dataservices', - BLIP_URL: 'https://app-qa2.development.tidepool.org' - } -}; - -export class App extends Component { - static propTypes = { - route: PropTypes.shape({ - api: PropTypes.func.isRequired - }).isRequired - }; - - constructor(props) { - super(props); - this.log = bows('App'); - const initial_server = _.findKey(serverdata, (key) => key.BLIP_URL === config.BLIP_URL); - this.state = { - server: initial_server - }; - - } - - UNSAFE_componentWillMount(){ - if(env.electron){ - checkVersion(this.props.dispatch); - } - let {api} = this.props; - this.props.async.doAppInit( - _.assign({ environment: this.state.server }, config), { - api: api, - carelink, - device, - localStore, - log: this.log - }); - - const addServers = (servers) => { - if (servers && servers.length && servers.length > 0) { - for (let server of servers) { - const protocol = server.name === 'localhost' ? 'http://' : 'https://'; - const url = protocol + server.name + ':' + server.port; - serverdata[server.name] = { - API_URL: url, - UPLOAD_URL: url, - DATA_URL: url + '/dataservices', - BLIP_URL: url, - }; - } - } else { - this.log('No servers found'); - } - }; - - - - if(env.electron_renderer){ - dns.resolveSrv('environments-srv.tidepool.org', (err, servers) => { - if (err) { - this.log(`DNS resolver error: ${err}. Retrying...`); - dns.resolveSrv('environments-srv.tidepool.org', (err2, servers2) => { - if (!err2) { - addServers(servers2); - } - }); - } else { - addServers(servers); - } - }); - } else { - var servers = [ - { name: 'localhost', port: 3000, priority: 5, weight: 10 }, - { name: 'dev1.dev.tidepool.org', port: 443, priority: 5, weight: 10 }, - { - name: 'external.integration.tidepool.org', - port: 443, - priority: 5, - weight: 10, - }, - { - name: 'qa1.development.tidepool.org', - port: 443, - priority: 5, - weight: 10, - }, - { - name: 'qa2.development.tidepool.org', - port: 443, - priority: 5, - weight: 10, - }, - ]; - addServers(servers); - this.setServer({label:'qa2.development.tidepool.org'}); - } - - - if(env.electron){ - window.addEventListener('contextmenu', this.handleContextMenu, false); - } - } - - setServer = info => { - console.log('will use', info.label, 'server'); - var serverinfo = serverdata[info.label]; - serverinfo.environment = info.label; - this.props.api.setHosts(serverinfo); - this.setState({server: info.label}); - }; - - render() { - return ( -
    -
    - - - - - - - - - -
    - {/* VersionCheck as overlay */} - {this.renderVersionCheck()} - - - - -
    - ); - } - - handleContextMenu = e => { - e.preventDefault(); - const { clientX, clientY } = e; - let template = []; - if (process.env.NODE_ENV === 'development') { - template.push({ - label: 'Inspect element', - click() { - remote.getCurrentWindow().inspectElement(clientX, clientY); - } - }); - template.push({ - type: 'separator' - }); - } - if (this.props.location.pathname === pagesMap.LOGIN) { - const submenus = []; - for (let server of _.keys(serverdata)) { - submenus.push({ - label: server, - click: this.setServer, - type: 'radio', - checked: this.state.server === server - }); - } - template.push({ - label: 'Change server', - submenu: submenus, - }); - template.push({ - label: 'Toggle Debug Mode', - type: 'checkbox', - checked: debugMode.isDebug, - click() { - debugMode.setDebug(!debugMode.isDebug); - } - }); - } - const menu = remote.Menu.buildFromTemplate(template); - menu.popup(remote.getCurrentWindow()); - }; - - handleDismissDropdown = () => { - const { dropdown } = this.props; - // only toggle the dropdown by clicking elsewhere if it's open - if (dropdown === true) { - this.props.sync.toggleDropdown(dropdown); - } - }; - - renderVersionCheck() { - const { readyToRenderVersionCheckOverlay, unsupported } = this.props; - if (readyToRenderVersionCheckOverlay === false || unsupported === false) { - return null; - } - if (unsupported instanceof Error) { - return ( - - ); - } - if (unsupported === true) { - return ( - - ); - } - } -} - -App.propTypes = {}; - -export default hot(module)(connect( - (state, ownProps) => { - return { - // plain state - dropdown: state.dropdown, - unsupported: state.unsupported, - // derived state - readyToRenderVersionCheckOverlay: ( - !state.working.initializingApp && !state.working.checkingVersion - ) - }; - }, - (dispatch) => { - return { - async: bindActionCreators(asyncActions, dispatch), - sync: bindActionCreators(syncActions, dispatch), - dispatch: dispatch - }; - } -)(App)); diff --git a/app/containers/ClinicUserEditPage.js b/app/containers/ClinicUserEditPage.js deleted file mode 100644 index 8eb0ed4a7..000000000 --- a/app/containers/ClinicUserEditPage.js +++ /dev/null @@ -1,82 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2014-2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { pages } from '../constants/otherConstants'; -import * as metrics from '../constants/metrics'; -import actions from '../actions/'; -import React, { Component } from 'react'; -import ClinicUserEdit from '../components/ClinicUserEdit'; - -const asyncActions = actions.async; -const syncActions = actions.sync; - -export class ClinicUserEditPage extends Component { - handleClickChangePerson = (metric = {metric: {eventName: metrics.CLINIC_SEARCH_DISPLAYED}}) => { - const { setUploadTargetUser } = this.props.sync; - const { setPage } = this.props.async; - setUploadTargetUser(null); - setPage(pages.CLINIC_USER_SELECT, undefined, metric); - }; - - render() { - const { allUsers, uploadTargetUser, memberships } = this.props; - return ( -
    - -
    - ); - } -} - -export default connect( - (state) => { - return { - allUsers: state.allUsers, - loggedInUser: state.loggedInUser, - targetUsersForUpload: state.targetUsersForUpload, - uploadTargetUser: state.uploadTargetUser, - updateProfileErrorMessage: state.updateProfileErrorMessage, - updateProfileErrorDismissed: state.updateProfileErrorDismissed, - createCustodialAccountErrorMessage: state.createCustodialAccountErrorMessage, - createCustodialAccountErrorDismissed: state.createCustodialAccountErrorDismissed, - memberships: state.memberships, - }; - }, - (dispatch) => { - return { - async: bindActionCreators(asyncActions, dispatch), - sync: bindActionCreators(syncActions, dispatch) - }; - } -)(ClinicUserEditPage); diff --git a/app/containers/ClinicUserSelectPage.js b/app/containers/ClinicUserSelectPage.js deleted file mode 100644 index a47d74161..000000000 --- a/app/containers/ClinicUserSelectPage.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2014-2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import actions from '../actions/'; -import React, { Component } from 'react'; -import ClinicUserSelect from '../components/ClinicUserSelect'; - -const asyncActions = actions.async; -const syncActions = actions.sync; - -export class ClinicUserSelectPage extends Component { - - render() { - const { allUsers, targetUsersForUpload, uploadTargetUser } = this.props; - return ( -
    - -
    - ); - } -} - -export default connect( - (state) => { - return { - allUsers: state.allUsers, - targetUsersForUpload: state.targetUsersForUpload, - uploadTargetUser: state.uploadTargetUser, - }; - }, - (dispatch) => { - return { - async: bindActionCreators(asyncActions, dispatch), - sync: bindActionCreators(syncActions, dispatch) - }; - } -)(ClinicUserSelectPage); diff --git a/app/containers/MainPage.js b/app/containers/MainPage.js deleted file mode 100644 index b5acfc236..000000000 --- a/app/containers/MainPage.js +++ /dev/null @@ -1,260 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2014-2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { pages } from '../constants/otherConstants'; -import * as actionSources from '../constants/actionSources'; -import * as metrics from '../constants/metrics'; -import actions from '../actions/'; -import ClinicUploadDone from '../components/ClinicUploadDone'; -import ClinicUserBlock from '../components/ClinicUserBlock'; -import cx from 'classnames'; -import React, { Component } from 'react'; -import styles from '../../styles/components/App.module.less'; -import TimezoneDropdown from '../components/TimezoneDropdown'; -import UploadList from '../components/UploadList'; -import ViewDataLink from '../components/ViewDataLink'; -import UserDropdown from '../components/UserDropdown'; -//const remote = require('@electron/remote'); - -const asyncActions = actions.async; -const syncActions = actions.sync; - -// const i18n = remote.getGlobal('i18n'); -let i18n = {t:string => string}; - -export class MainPage extends Component { - handleClickEditUser = () => { - const { setPage } = this.props.async; - setPage(pages.CLINIC_USER_EDIT, undefined, {metric: {eventName: metrics.CLINIC_EDIT_INFO}}); - }; - - handleClickChangePerson = (metric = {metric: {eventName: metrics.CLINIC_SEARCH_DISPLAYED}}) => { - const { setUploadTargetUser } = this.props.sync; - const { setPage } = this.props.async; - setUploadTargetUser(null); - setPage(pages.CLINIC_USER_SELECT, undefined, metric); - }; - - handleClickChooseDevices = metric => { - const { toggleDropdown } = this.props.sync; - const { setPage } = this.props.async; - // ensure dropdown closes after click - setPage(pages.SETTINGS, true, metric); - toggleDropdown(true, actionSources.UNDER_THE_HOOD); - }; - - renderTimezoneDropdown() { - const { uploadTargetUser } = this.props; - return ( - - ); - } - - renderUploadListDoneButton() { - const { isClinicAccount } = this.props; - if (isClinicAccount && this.props.uploadTargetUser) { - return ; - } else { - const viewDataLink = _.get(this.props, ['blipUrls', 'viewDataLink'], ''); - return ; - } - } - - renderUserDropdown() { - const { allUsers, targetUsersForUpload, uploadTargetUser, location } = this.props; - return ( - - ); - } - - renderChangePersonLink() { - var classes = cx({ - [styles.changePerson]: true, - [styles.linkDisabled]: this.props.uploadIsInProgress - }); - return ( -
    {i18n.t('Change Person')}
    - ); - } - - renderClinicUserBlock() { - const { isClinicAccount } = this.props; - if (!isClinicAccount) return null; - let timezoneDropdown = this.renderTimezoneDropdown(); - return ( - - ); - } - - render() { - let changePersonLink = null; - let clinicUserBlock = null; - - if(this.props.isClinicAccount){ - changePersonLink = this.renderChangePersonLink(); - clinicUserBlock = this.renderClinicUserBlock(); - } - - let userDropdown = this.props.showingUserSelectionDropdown ? - this.renderUserDropdown() : null; - - let timezoneDropdown = null; - let viewDataLinkButton = this.renderUploadListDoneButton(); - if(!this.props.isClinicAccount){ - timezoneDropdown = this.renderTimezoneDropdown(); - } - return ( -
    - {userDropdown} - {timezoneDropdown} - {changePersonLink} - {clinicUserBlock} - - {viewDataLinkButton} -
    - ); - } -} - -export default connect( - (state) => { - function getSelectedTimezone(state) { - return _.get( - state, - ['targetTimezones', state.uploadTargetUser], - // fall back to the timezone stored under 'noUserSelected', if any - _.get(state, ['targetTimezones', 'noUserSelected'], null) - ); - } - function getActiveUploads(state) { - const { devices, uploadsByUser, uploadTargetUser } = state; - if (uploadTargetUser === null) { - return []; - } - let activeUploads = []; - const targetUsersUploads = _.get(uploadsByUser, uploadTargetUser, []); - _.map(_.keys(targetUsersUploads), (deviceKey) => { - const upload = uploadsByUser[uploadTargetUser][deviceKey]; - const device = _.pick(devices[deviceKey], ['instructions', 'image', 'key', 'name', 'source']); - const progress = upload.uploading ? {progress: state.uploadProgress} : - (upload.successful ? {progress: {percentage: 100}} : {}); - activeUploads.push(_.assign({}, device, upload, progress)); - }); - // ensure that carelink is last - const carelink = _.remove(activeUploads, {'key': 'carelink'}); - if(!_.isEmpty(carelink)){ - activeUploads = activeUploads.concat(carelink); - } - return activeUploads; - } - function shouldShowUserSelectionDropdown(state) { - if (!_.isEmpty(state.targetUsersForUpload) && !isClinicAccount(state)) { - // if there's only one potential target for upload but it's *not* the loggedInUser - if (state.targetUsersForUpload.length === 1 && - !_.includes(state.targetUsersForUpload, state.loggedInUser)) { - return true; - } - if (state.targetUsersForUpload.length > 1) { - return true; - } - } - return false; - } - function isClinicAccount(state) { - return _.indexOf(_.get(_.get(state.allUsers, state.loggedInUser, {}), 'roles', []), 'clinic') !== -1; - } - return { - activeUploads: getActiveUploads(state), - allUsers: state.allUsers, - memberships: state.memberships, - blipUrls: state.blipUrls, - isClinicAccount: isClinicAccount(state), - isTimezoneFocused: state.isTimezoneFocused, - page: state.page, - selectedTimezone: getSelectedTimezone(state), - showingUserSelectionDropdown: shouldShowUserSelectionDropdown(state), - targetUsersForUpload: state.targetUsersForUpload, - unsupported: state.unsupported, - updateProfileErrorMessage: state.updateProfileErrorMessage, - uploadIsInProgress: state.working.uploading, - uploadTargetUser: state.uploadTargetUser, - uploadsByUser: state.uploadsByUser, - }; - }, - (dispatch) => { - return { - async: bindActionCreators(asyncActions, dispatch), - sync: bindActionCreators(syncActions, dispatch) - }; - } -)(MainPage); diff --git a/app/containers/NoUploadTargetsPage.js b/app/containers/NoUploadTargetsPage.js deleted file mode 100644 index b971bc31f..000000000 --- a/app/containers/NoUploadTargetsPage.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2014-2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; -import { connect } from 'react-redux'; -import React, { Component } from 'react'; -import NoUploadTargets from '../components/NoUploadTargets'; - -export class NoUploadTargetsPage extends Component { - - render() { - const newPatientLink = _.get(this.props, ['blipUrls', 'newPatient'], ''); - return ( -
    - -
    - ); - } -} - -export default connect( - (state) => { - return { - blipUrls: state.blipUrls, - }; - } -)(NoUploadTargetsPage); diff --git a/app/containers/SettingsPage.js b/app/containers/SettingsPage.js deleted file mode 100644 index f0c66f3d7..000000000 --- a/app/containers/SettingsPage.js +++ /dev/null @@ -1,181 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2014, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; -import React, { Component } from 'react'; -import DeviceSelection from '../components/DeviceSelection'; -import UserDropdown from '../components/UserDropdown'; -import ClinicUserBlock from '../components/ClinicUserBlock'; -import cx from 'classnames'; -import styles from '../../styles/components/App.module.less'; -import { pages } from '../constants/otherConstants'; -import * as metrics from '../constants/metrics'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import actions from '../actions/'; -//const remote = require('@electron/remote'); - -const asyncActions = actions.async; -const syncActions = actions.sync; - -// const i18n = remote.getGlobal('i18n'); -let i18n = {t:string => string}; - -export class SettingsPage extends Component { - handleClickChangePerson = (metric = {metric: {eventName: metrics.CLINIC_SEARCH_DISPLAYED}}) => { - const { setUploadTargetUser } = this.props.sync; - const { setPage } = this.props.async; - setUploadTargetUser(null); - setPage(pages.CLINIC_USER_SELECT, undefined, metric); - }; - - handleClickEditUser = () => { - const { setPage } = this.props.async; - setPage(pages.CLINIC_USER_EDIT, undefined, {metric: {eventName: metrics.CLINIC_EDIT_INFO}}); - }; - - renderChangePersonLink() { - var classes = cx({ - [styles.changePerson]: true, - [styles.linkDisabled]: this.props.uploadIsInProgress - }); - return ( -
    {i18n.t('Change Person')}
    - ); - } - - renderClinicUserBlock() { - const { isClinicAccount } = this.props; - if (!isClinicAccount) return null; - return ( - - ); - } - - renderUserDropdown() { - const { allUsers, targetUsersForUpload, uploadTargetUser, location } = this.props; - return ( - - ); - } - - render() { - let userDropdown = this.props.showingUserSelectionDropdown ? - this.renderUserDropdown() : null; - - let changePersonLink = null; - let clinicUserBlock = null; - - if(this.props.isClinicAccount){ - changePersonLink = this.renderChangePersonLink(); - clinicUserBlock = this.renderClinicUserBlock(); - } - - return ( -
    - {userDropdown} - {changePersonLink} - {clinicUserBlock} - -
    - ); - } -} - -export default connect( - (state) => { - function getSelectedTargetDevices(state) { - return _.get( - state, - ['targetDevices', state.uploadTargetUser], - // fall back to the targets stored under 'noUserSelected', if any - _.get(state, ['targetDevices', 'noUserSelected'], []) - ); - } - function getSelectedTimezone(state) { - return _.get( - state, - ['targetTimezones', state.uploadTargetUser], - // fall back to the timezone stored under 'noUserSelected', if any - _.get(state, ['targetTimezones', 'noUserSelected'], null) - ); - } - function isClinicAccount(state) { - return _.indexOf(_.get(_.get(state.allUsers, state.loggedInUser, {}), 'roles', []), 'clinic') !== -1; - } - function shouldShowUserSelectionDropdown(state) { - if (!_.isEmpty(state.targetUsersForUpload) && !isClinicAccount(state)) { - // if there's only one potential target for upload but it's *not* the loggedInUser - if (state.targetUsersForUpload.length === 1 && - !_.includes(state.targetUsersForUpload, state.loggedInUser)) { - return true; - } - if (state.targetUsersForUpload.length > 1) { - return true; - } - } - return false; - } - return { - allUsers: state.allUsers, - devices: state.devices, - disabled: Boolean(state.unsupported), - errorMessage: state.loginErrorMessage, - forgotPasswordUrl: state.blipUrls.forgotPassword, - isClinicAccount: isClinicAccount(state), - isFetching: state.working.fetchingUserInfo, - page: state.page, - selectedTimezone: getSelectedTimezone(state), - showingUserSelectionDropdown: shouldShowUserSelectionDropdown(state), - targetDevices: getSelectedTargetDevices(state), - targetUsersForUpload: state.targetUsersForUpload, - uploadIsInProgress: state.working.uploading, - uploadTargetUser: state.uploadTargetUser, - }; - }, - (dispatch) => { - return { - async: bindActionCreators(asyncActions, dispatch), - sync: bindActionCreators(syncActions, dispatch) - }; - } -)(SettingsPage); diff --git a/app/containers/Top.js b/app/containers/Top.js deleted file mode 100644 index a1bcb480b..000000000 --- a/app/containers/Top.js +++ /dev/null @@ -1,43 +0,0 @@ -import { hot } from 'react-hot-loader'; -import rollbar from '../utils/rollbar'; -import _ from 'lodash'; -import React, { Fragment } from 'react'; -// import { AppContainer as ReactHotAppContainer } from 'react-hot-loader'; -import { render } from 'react-dom'; -import { Provider } from 'react-redux'; -import { Route } from 'react-router-dom'; -import { push } from 'connected-react-router'; -// import { ipcRenderer } from 'electron'; -import { ConnectedRouter } from 'connected-react-router'; -import { createHashHistory } from 'history'; - -import config from '../../lib/config'; -window.DEBUG = config.DEBUG; -import configureStore from '../store/configureStore'; -import api from '../../lib/core/api'; -import App from './App'; -import '..//app.global.css'; -import '../../styles/main.less'; - - -const history = createHashHistory(); - -const store = configureStore(undefined, history); -store.dispatch(push('/')); - -// This is the communication mechanism for receiving actions dispatched from -// the `main` Electron process. `action` should always be the resulting object -// from an action creator. -// ipcRenderer.on('action', function(event, action) { -// store.dispatch(action); -// }); - -// const AppContainer = process.env.PLAIN_HMR ? Fragment : ReactHotAppContainer; -const Top = () => ( - - - } > - - -); -export default hot(module)(Top); diff --git a/app/index.js b/app/index.js old mode 100755 new mode 100644 index b0d3931f4..822bc598d --- a/app/index.js +++ b/app/index.js @@ -1,40 +1,160 @@ -// import rollbar from './utils/rollbar'; -// import _ from 'lodash'; -import React, { Fragment } from 'react'; -// import { AppContainer as ReactHotAppContainer } from 'react-hot-loader'; -import { render } from 'react-dom'; -// import { Provider } from 'react-redux'; -// import { Route } from 'react-router-dom'; -// import { push } from 'connected-react-router'; -// // import { ipcRenderer } from 'electron'; -// import { ConnectedRouter } from 'connected-react-router'; -// import { createHashHistory } from 'history'; - -// import config from '../lib/config'; -// window.DEBUG = config.DEBUG; -// import configureStore from './store/configureStore'; -// import api from '../lib/core/api'; -// import App from './containers/App'; -// import './app.global.css'; -// import '../styles/main.less'; - -import Top from './containers/Top'; - -// const history = createHashHistory(); - -// const store = configureStore(undefined, history); -// store.dispatch(push('/')); - -// This is the communication mechanism for receiving actions dispatched from -// the `main` Electron process. `action` should always be the resulting object -// from an action creator. -// ipcRenderer.on('action', function(event, action) { -// store.dispatch(action); -// }); - -// const AppContainer = process.env.PLAIN_HMR ? Fragment : ReactHotAppContainer; - -render( - , - document.getElementById('app') -); +import _ from 'lodash'; +import device from '../lib/core/device'; +import driverManifests from '../lib/core/driverManifests'; +import api from '../lib/core/api'; +import builder from '../lib/objectBuilder'; + +const button = document.getElementById('connect'); +const login = document.getElementById('login'); +const app = document.getElementById('app'); +const progressBar = document.getElementById('progressBar'); +const select = document.getElementById('devices'); + +let driverId = 'BayerContourNext'; + +const options = { + api, + timezone: 'Europe/London', + version: 'uploader web2', + builder: builder(), + progress: makeProgress(), +}; + +const config = { + API_URL: 'https://qa2.development.tidepool.org', + UPLOAD_URL: 'https://qa2.development.tidepool.org', + DATA_URL: 'https://qa2.development.tidepool.org/dataservices', + BLIP_URL: 'https://app-qa2.development.tidepool.org' +}; + +api.create({ + apiUrl: config.API_URL, + uploadUrl: config.UPLOAD_URL, + dataUrl: config.DATA_URL, + version: 'uploader web2', +}); + +login.addEventListener('submit', (event) => { + const username = login.elements['username'].value; + const password = login.elements['password'].value; + + api.init(() => { + api.user.login({ + username, + password + }, (error, loginData) => { + if (error) { + console.log(error); + } else { + options.targetId = loginData.userid; + options.groupId = loginData.userid; + + populateDevices(); + + login.setAttribute('hidden', ''); + app.removeAttribute('hidden'); + } + }); + }); + + event.preventDefault(); +}); + +function makeProgress() { + return (step, percentage, isFirstUpload) => { + progressBar.value = percentage; + }; +} + +function initUpload(driverId) { + device.init(options, () => { + device.detect(driverId, options, (error, deviceInfo) => { + if (deviceInfo !== undefined) { + console.log('deviceInfo: ', deviceInfo); + options.deviceInfo = deviceInfo; + device.upload(driverId, options, (error) => { + if (error) { + console.error(`Error: ${error}`); + } + }); + } else { + console.error(`Error: ${error}`); + } + }); + }); +} + +function populateDevices() { + for (const driver of Object.keys(driverManifests)) { + const opt = document.createElement('option'); + opt.value = driver; + opt.text = driver; + + if (driver === driverId) { + opt.setAttribute('selected', ''); + } + select.add(opt, null); + } +} + +select.addEventListener('change', () => { + driverId = select.options[select.selectedIndex].value; +}); + +button.addEventListener('click', async() => { + + const driverManifest = _.get(driverManifests, driverId); + + const filters = driverManifest.usb.map(({vendorId, productId}) => ({ + usbVendorId: vendorId, + usbProductId: productId + })); + + if (driverManifest && driverManifest.mode === 'serial') { + try { + const existingPermissions = await navigator.serial.getPorts(); + + for (let i = 0; i < existingPermissions.length; i++) { + const { usbProductId, usbVendorId } = existingPermissions[i].getInfo(); + + for (let j = 0; j < driverManifest.usb.length; j++) { + if (driverManifest.usb[j].vendorId === usbVendorId + && driverManifest.usb[j].productId === usbProductId) { + console.log('Device has already been granted permission'); + options.port = existingPermissions[i]; + } + } + } + + initUpload(driverId); + } catch (err) { + console.log('Error:', err); + } + } else { + try { + const existingPermissions = await navigator.hid.getDevices(); + + for (let i = 0; i < existingPermissions.length; i++) { + for (let j = 0; j < driverManifest.usb.length; j++) { + if (driverManifest.usb[j].vendorId === existingPermissions[i].vendorId + && driverManifest.usb[j].productId === existingPermissions[i].productId) { + console.log('Device has already been granted permission'); + options.hidDevice = existingPermissions[i]; + } + } + } + + if (options.hidDevice == null) { + [options.hidDevice] = await navigator.hid.requestDevice({ filters: filters }); + } + + if (options.hidDevice == null) { + throw new Error('No device was selected.'); + } + + initUpload(driverId); + } catch (err) { + console.log('Error:', err); + } + } +}); diff --git a/app/main.dev.js b/app/main.dev.js deleted file mode 100755 index 7fa7df997..000000000 --- a/app/main.dev.js +++ /dev/null @@ -1,551 +0,0 @@ -/* global __ROLLBAR_POST_TOKEN__ */ -import _ from 'lodash'; -import { app, BrowserWindow, Menu, shell, ipcMain, crashReporter, dialog, session } from 'electron'; -import os from 'os'; -import osName from 'os-name'; -import open from 'open'; -import { autoUpdater } from 'electron-updater'; -import * as chromeFinder from 'chrome-launcher/dist/chrome-finder'; -import { sync as syncActions } from './actions'; -import debugMode from '../app/utils/debugMode'; -import Rollbar from 'rollbar/src/server/rollbar'; -import uploadDataPeriod from './utils/uploadDataPeriod'; -import i18n from 'i18next'; -import i18nextBackend from 'i18next-fs-backend'; -import i18nextOptions from './utils/config.i18next'; -import path from 'path'; - -global.i18n = i18n; - -autoUpdater.logger = require('electron-log'); -autoUpdater.logger.transports.file.level = 'info'; -require('@electron/remote/main').initialize(); - -let rollbar; -if(process.env.NODE_ENV === 'production') { - rollbar = new Rollbar({ - accessToken: __ROLLBAR_POST_TOKEN__, - captureUncaught: true, - captureUnhandledRejections: true, - payload: { - environment: 'electron_main_process' - } - }); -} - -crashReporter.start({ - productName: 'Uploader', - companyName: 'Tidepool', - submitURL: '', - uploadToServer: false -}); - -console.log('Crash logs can be found in:', app.getPath('crashDumps')); -console.log('Last crash report:', crashReporter.getLastCrashReport()); - -let menu; -let template; -let mainWindow = null; - -// Web Bluetooth should only be an experimental feature on Linux -app.commandLine.appendSwitch('enable-experimental-web-platform-features', true); -app.commandLine.appendSwitch('enable-features', 'ElectronSerialChooser'); - -// as of March 2021, node-usb is not yet context-aware, see -// https://github.com/tessel/node-usb/issues/380 for details -app.allowRendererProcessReuse = false; - -if (process.env.NODE_ENV === 'production') { - const sourceMapSupport = require('source-map-support'); // eslint-disable-line - sourceMapSupport.install(); -} - -if (process.env.NODE_ENV === 'development') { - require('electron-debug')(); // eslint-disable-line global-require - const p = path.join(__dirname, '..', 'app', 'node_modules'); // eslint-disable-line - require('module').globalPaths.push(p); // eslint-disable-line -} - -app.on('window-all-closed', () => { - if (process.platform !== 'darwin') app.quit(); -}); - -const installExtensions = async () => { - if (process.env.NODE_ENV === 'development') { - const { default: installExtension, REDUX_DEVTOOLS } = require('electron-devtools-installer'); - - try { - const name = await installExtension([REDUX_DEVTOOLS]); - console.log(`Added Extension: ${name}`); - - // electron-devtools-installer fails to install React Developer Tools on Electron v12, - // so for now we install it manually - await session.defaultSession.loadExtension( - path.join(__dirname, '..', 'extensions', 'react-devtools'), - // allowFileAccess is required to load the devtools extension on file:// URLs. - { allowFileAccess: true } - ); - console.log('Added Extension: React Developer Tools'); - } catch (err) { - console.log('An error occurred: ', err); - } - } -}; - -function addDataPeriodGlobalListener(menu) { - ipcMain.on('setUploadDataPeriodGlobal', (event, arg) => { - const item = _.find(menu.items, ['id', 'upload']); - if (arg === uploadDataPeriod.PERIODS.ALL) { - console.log('Uploading all data'); - item.submenu.items[0].checked = true; - } else if (arg === uploadDataPeriod.PERIODS.DELTA) { - console.log('Uploading only new records'); - item.submenu.items[1].checked = true; - } - }); -}; - -app.on('ready', async () => { - await installExtensions(); - setLanguage(); -}); - -function createWindow() { - const resizable = (process.env.NODE_ENV === 'development'); - mainWindow = new BrowserWindow({ - show: false, - width: 663, - height: 769, - resizable: resizable, - webPreferences: { - nodeIntegration: true, - contextIsolation: false, // so that we can access process from app.html - enableRemoteModule: true, - } - }); - - mainWindow.webContents.on('render-process-gone', (e, details) => { - console.log('Render process gone:', details.reason); - }); - - mainWindow.loadURL(`file://${__dirname}/app.html`); - - mainWindow.webContents.on('did-finish-load', async () => { - if (osName() === 'Windows 7') { - const options = { - type: 'info', - title: 'Please update to a modern operating system', - message: - `Windows 7 won't be patched for any new viruses or security problems -going forward. - -While Windows 7 will continue to work, Microsoft recommends you -start planning to upgrade to Windows 10, or an alternative -operating system, as soon as possible.`, - buttons: ['Continue'] - }; - await dialog.showMessageBox(options); - } - - mainWindow.show(); - mainWindow.focus(); - checkUpdates(); - }); - - mainWindow.webContents.on('new-window', function(event, url){ - event.preventDefault(); - let platform = os.platform(); - let chromeInstalls = chromeFinder[platform](); - if(chromeInstalls.length === 0){ - // no chrome installs found, open user's default browser - open(url); - } else { - open(url, {app: chromeInstalls[0]}, function(error){ - if(error){ - // couldn't open chrome, try OS default - open(url); - } - }); - } - }); - mainWindow.on('closed', () => { - mainWindow = null; - }); - - mainWindow.webContents.on('select-bluetooth-device', (event, deviceList, webContents, callback) => { - event.preventDefault(); - console.log('Device list:', deviceList); - let [result] = deviceList; - global.bluetoothDeviceId = result.deviceId; - if (!result) { - callback(''); - } else { - callback(result.deviceId); - } - }); - - mainWindow.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => { - event.preventDefault(); - console.log('Port list:', portList); - const [selectedPort] = portList; - if (!selectedPort) { - callback(''); - } else { - callback(selectedPort.portId); - } - }); - - if (process.env.NODE_ENV === 'development') { - mainWindow.openDevTools(); - mainWindow.webContents.on('context-menu', (e, props) => { - const { x, y } = props; - - Menu.buildFromTemplate([{ - label: 'Inspect element', - click() { - mainWindow.inspectElement(x, y); - } - }]).popup(mainWindow); - }); - } - - if (process.platform === 'darwin') { - template = [{ - label: i18n.t('Tidepool Uploader'), - submenu: [{ - label: i18n.t('About Tidepool Uploader'), - selector: 'orderFrontStandardAboutPanel:' - }, { - label: i18n.t('Check for Updates'), - click() { - manualCheck = true; - autoUpdater.checkForUpdates(); - } - }, { - type: 'separator' - }, { - label: i18n.t('Hide Tidepool Uploader'), - accelerator: 'Command+H', - selector: 'hide:' - }, { - label: i18n.t('Hide Others'), - accelerator: 'Command+Shift+H', - selector: 'hideOtherApplications:' - }, { - label: i18n.t('Show All'), - selector: 'unhideAllApplications:' - }, { - type: 'separator' - }, { - label: i18n.t('Quit'), - accelerator: 'Command+Q', - click() { - app.quit(); - } - }] - }, { - label: i18n.t('Edit'), - submenu: [{ - label: i18n.t('Undo'), - accelerator: 'Command+Z', - selector: 'undo:' - }, { - label: i18n.t('Redo'), - accelerator: 'Shift+Command+Z', - selector: 'redo:' - }, { - type: 'separator' - }, { - label: i18n.t('Cut'), - accelerator: 'Command+X', - selector: 'cut:' - }, { - label: i18n.t('Copy'), - accelerator: 'Command+C', - selector: 'copy:' - }, { - label: i18n.t('Paste'), - accelerator: 'Command+V', - selector: 'paste:' - }, { - label: i18n.t('Select All'), - accelerator: 'Command+A', - selector: 'selectAll:' - }] - }, { - label: i18n.t('View'), - submenu: (process.env.NODE_ENV === 'development') ? - [ - { - label: i18n.t('Reload'), - accelerator: 'Command+R', - click() { - mainWindow.webContents.reload(); - } - }, { - label: i18n.t('Toggle Full Screen'), - accelerator: 'Ctrl+Command+F', - click() { - mainWindow.setFullScreen(!mainWindow.isFullScreen()); - } - }, { - label: i18n.t('Toggle Developer Tools'), - accelerator: 'Alt+Command+I', - click() { - mainWindow.toggleDevTools(); - } - } - ] : [ - { - label: i18n.t('Toggle Full Screen'), - accelerator: 'Ctrl+Command+F', - click() { - mainWindow.setFullScreen(!mainWindow.isFullScreen()); - } - }, { - label: i18n.t('Toggle Developer Tools'), - accelerator: 'Alt+Command+I', - click() { - mainWindow.toggleDevTools(); - } - } - ] - }, { - label: i18n.t('&Upload'), - id: 'upload', - submenu: [{ - label: i18n.t('All data'), - type: 'radio', - click() { - console.log('Uploading all data'); - uploadDataPeriod.setPeriodGlobal( - uploadDataPeriod.PERIODS.ALL, mainWindow); - } - }, { - label: i18n.t('Data since last upload'), - type: 'radio', - click() { - console.log('Uploading only new records'); - uploadDataPeriod.setPeriodGlobal( - uploadDataPeriod.PERIODS.DELTA, mainWindow); - } - }] - }, { - label: i18n.t('Window'), - submenu: [{ - label: i18n.t('Minimize'), - accelerator: 'Command+M', - selector: 'performMiniaturize:' - }, { - label: i18n.t('Close'), - accelerator: 'Command+W', - selector: 'performClose:' - }, { - type: 'separator' - }, { - label: i18n.t('Bring All to Front'), - selector: 'arrangeInFront:' - }] - }, { - label: i18n.t('Help'), - submenu: [{ - label: i18n.t('Get Support'), - click() { - shell.openExternal('http://support.tidepool.org/'); - } - }, { - label: i18n.t('Privacy Policy'), - click() { - shell.openExternal('https://developer.tidepool.org/privacy-policy/'); - } - }] - }]; - - menu = Menu.buildFromTemplate(template); - addDataPeriodGlobalListener(menu); - Menu.setApplicationMenu(menu); - } else { - template = [{ - label: i18n.t('&File'), - submenu: [{ - label: i18n.t('&Open'), - accelerator: 'Ctrl+O' - }, { - label: i18n.t('&Close'), - accelerator: 'Ctrl+W', - click() { - mainWindow.close(); - } - }] - }, { - label: i18n.t('&View'), - submenu: (process.env.NODE_ENV === 'development') ? [{ - label: i18n.t('&Reload'), - accelerator: 'Ctrl+R', - click() { - mainWindow.webContents.reload(); - } - }, { - label: i18n.t('Toggle &Full Screen'), - accelerator: 'F11', - click() { - mainWindow.setFullScreen(!mainWindow.isFullScreen()); - } - }, { - label: i18n.t('Toggle &Developer Tools'), - accelerator: 'Alt+Ctrl+I', - click() { - mainWindow.toggleDevTools(); - } - }] : [{ - label: i18n.t('Toggle &Full Screen'), - accelerator: 'F11', - click() { - mainWindow.setFullScreen(!mainWindow.isFullScreen()); - } - }, { - label: i18n.t('Toggle &Developer Tools'), - accelerator: 'Alt+Ctrl+I', - click() { - mainWindow.toggleDevTools(); - } - }] - }, { - label: i18n.t('&Upload'), - id: 'upload', - submenu: [{ - label: i18n.t('All data'), - type: 'radio', - click() { - console.log('Uploading all data'); - uploadDataPeriod.setPeriodGlobal( - uploadDataPeriod.PERIODS.ALL, mainWindow); - } - }, { - label: i18n.t('Data since last upload'), - type: 'radio', - click() { - console.log('Uploading only new records'); - uploadDataPeriod.setPeriodGlobal( - uploadDataPeriod.PERIODS.DELTA, mainWindow); - } - }] - }, { - label: i18n.t('Help'), - submenu: [{ - label: i18n.t('Get Support'), - click() { - shell.openExternal('http://support.tidepool.org/'); - } - }, { - label: i18n.t('Check for Updates'), - click() { - manualCheck = true; - autoUpdater.checkForUpdates(); - } - }, { - label: i18n.t('Privacy Policy'), - click() { - shell.openExternal('https://developer.tidepool.org/privacy-policy/'); - } - }] - }]; - menu = Menu.buildFromTemplate(template); - addDataPeriodGlobalListener(menu); - mainWindow.setMenu(menu); - } -} - -function checkUpdates(){ - // in production NODE_ENV we check for updates, but not if NODE_ENV is 'development' - // this prevents a Webpack build error that masks other build errors during local development - if (process.env.NODE_ENV === 'production') { - autoUpdater.checkForUpdates(); - } -} - -setInterval(checkUpdates, 1000 * 60 * 60 * 24); - -let manualCheck = false; - -function sendAction(action) { - mainWindow.webContents.send('action', action); -} - -autoUpdater.on('checking-for-update', () => { - if(manualCheck) { - manualCheck = false; - sendAction(syncActions.manualCheckingForUpdates()); - } else { - sendAction(syncActions.autoCheckingForUpdates()); - } -}); - -autoUpdater.on('update-available', (ev, info) => { - sendAction(syncActions.updateAvailable(info)); - /* - Example `info` - { - "version":"0.310.0-alpha", - "releaseDate":"2017-04-03T22:29:55.809Z", - "url":"https://github.com/tidepool-org/uploader/releases/download/v0.310.0-alpha/tidepool-uploader-dev-0.310.0-alpha-mac.zip", - "releaseJsonUrl":"https://github.com//tidepool-org/uploader/releases/download/v0.310.0-alpha/latest-mac.json" - } - */ -}); - -autoUpdater.on('update-not-available', (ev, info) => { - sendAction(syncActions.updateNotAvailable(info)); -}); - -autoUpdater.on('error', (ev, err) => { - sendAction(syncActions.autoUpdateError(err)); -}); - -autoUpdater.on('update-downloaded', (ev, info) => { - sendAction(syncActions.updateDownloaded(info)); -}); - -ipcMain.on('autoUpdater', (event, arg) => { - if(arg === 'checkForUpdates') { - manualCheck = true; - } - autoUpdater[arg](); -}); - -if(!app.isDefaultProtocolClient('tidepoolupload')){ - app.setAsDefaultProtocolClient('tidepoolupload'); -} - -app.on('window-all-closed', () => { - app.quit(); -}); - -app.on('activate', () => { - // for mac because, normally it's not common to recreate a window in the app - if (mainWindow === null) { - createWindow(); - } -}); - -function setLanguage() { - if (process.env.I18N_ENABLED === 'true') { - let lng = app.getLocale(); - // remove country in language locale - if (_.includes(lng,'-')) - lng = (_.split(lng,'-').length > 0) ? _.split(lng,'-')[0] : lng; - - i18nextOptions['lng'] = lng; - } - - if (!i18n.Initialize) { - i18n.use(i18nextBackend).init(i18nextOptions, function(err, t) { - if (err) { - console.log('An error occurred in i18next:', err); - } - - global.i18n = i18n; - createWindow(); - }); - } -} diff --git a/app/package.json b/app/package.json deleted file mode 100644 index 11a07496c..000000000 --- a/app/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "tidepool-uploader", - "productName": "tidepool-uploader", - "version": "2.36.1-lzo-wasm.1", - "description": "Tidepool Project Universal Uploader", - "main": "./main.prod.js", - "author": { - "name": "Tidepool Project", - "email": "gerrit@tidepool.org" - }, - "license": "BSD-2-Clause", - "dependencies": { - "drivelist": "9.2.4", - "keytar": "5.6.0", - "node-hid": "2.1.1", - "@ronomon/direct-io": "3.0.1", - "usb": "1.6.5", - "lzo-decompress": "1.0.1" - }, - "optionalDependencies": { - "node-mtp": "0.3.15", - "winreg": "1.2.4" - } -} diff --git a/app/reducers/devices.js b/app/reducers/devices.js deleted file mode 100644 index 559be576f..000000000 --- a/app/reducers/devices.js +++ /dev/null @@ -1,182 +0,0 @@ -import mm723Image from '../../images/MM723_CNL_combo@2x.jpg'; -import mm600Image from '../../images/MM600_CNL_combo@2x.jpg'; -//const remote = require('@electron/remote'); - -// // const i18n = remote.getGlobal( 'i18n' ); -let i18n = {t:string => string}; -const devices = { - accuchekusb: { - instructions: i18n.t('Plug in meter with micro-USB cable'), - name: 'Roche Accu-Chek Aviva Connect, Guide & Guide Me', - key: 'accuchekusb', - source: {type: 'device', driverId: 'AccuChekUSB'}, - enabled: {mac: true, win: true, linux: true}, - powerOnlyWarning: true, // shows warning for power-only USB cables - }, - carelink: { - instructions: [i18n.t('Import from CareLink'), i18n.t('(We will not store your credentials)')], - isFetching: false, - key: 'carelink', - name: 'Medtronic', - // for the device selection list - selectName: 'Medtronic (CareLink import)', - source: {type: 'carelink'}, - enabled: {mac: true, win: true, linux: true} - }, - caresensble: { - instructions: i18n.t('Once paired, hold in right arrow until "BT Send" appears on the screen'), - name: 'CareSens N Premier & Dual (using Bluetooth)', - key: 'caresensble', - source: {type: 'device', driverId: 'BluetoothLE'}, - enabled: {mac: true, win: false, linux: true} - }, - caresens: { - instructions: 'Plug in meter with cable and make sure the meter is switched on', - name: 'CareSens N Premier & Dual', - key: 'caresens', - source: {type: 'device', driverId: 'CareSens'}, - enabled: {mac: true, win: true, linux: true} - }, - medtronic: { - instructions: i18n.t('Connect your Contour Next Link to your computer'), - image: { - 'src': mm723Image, - 'height': 128, - 'width': 200, - 'alt': 'Contour Next Link' - }, - key: 'medtronic', - name: 'Medtronic 523, 723, Veo or 530G', - selectName: 'Medtronic 523, 723, Veo or 530G (using Contour Next Link)', - source: {type: 'device', driverId: 'Medtronic'}, - enabled: {mac: true, win: true, linux: true} - }, - medtronic600: { - instructions: i18n.t('Connect your Contour Next Link 2.4 to your computer'), - image: { - 'src': mm600Image, - 'height': 128, - 'width': 200, - 'alt': 'Bayer Contour Next Link 2.4' - }, - key: 'medtronic600', - name: 'Medtronic 630G, 640G or 670G', - selectName: 'Medtronic 630G, 640G, 670G (using Contour Next Link 2.4)', - showDriverLink: {mac: false, win: false}, - source: {type: 'device', driverId: 'Medtronic600'}, - enabled: {mac: true, win: true, linux: true} - }, - omnipod: { - instructions: [i18n.t('Classic PDM: Plug into USB. Wait for Export to complete. Click Upload.'), i18n.t('DASH PDM: Unlock. Plug into USB. Tap Export on PDM. Click Upload.')], - key: 'omnipod', - name: 'Insulet OmniPod', - source: {type: 'device', driverId: 'InsuletOmniPod', extension: '.ibf'}, - enabled: {mac: true, win: true, linux: true}, - powerOnlyWarning: true, - }, - dexcom: { - instructions: i18n.t('Plug in receiver with micro-USB'), - key: 'dexcom', - name: 'Dexcom', - source: {type: 'device', driverId: 'Dexcom'}, - enabled: {mac: true, win: true, linux: true} - }, - precisionxtra: { - instructions: i18n.t('Plug in meter with cable'), - key: 'precisionxtra', - name: 'Abbott Precision Xtra', - source: {type: 'device', driverId: 'AbbottPrecisionXtra'}, - enabled: {mac: false, win: true, linux: true} - }, - tandem: { - instructions: i18n.t('Plug in pump with micro-USB'), - key: 'tandem', - name: 'Tandem', - source: {type: 'device', driverId: 'Tandem'}, - enabled: {mac: true, win: true, linux: true}, - powerOnlyWarning: true, - }, - abbottfreestylelite: { - instructions: i18n.t('Plug in meter with cable'), - key: 'abbottfreestylelite', - name: 'Abbott FreeStyle Lite & Freedom Lite', - source: {type: 'device', driverId: 'AbbottFreeStyleLite'}, - enabled: {mac: false, win: true, linux: true} - }, - abbottfreestylelibre: { - instructions: i18n.t('Plug in meter with micro-USB cable'), - key: 'abbottfreestylelibre', - name: 'Abbott FreeStyle Libre', - source: {type: 'device', driverId: 'AbbottFreeStyleLibre'}, - enabled: {linux: true, mac: true, win: true}, - powerOnlyWarning: true, - }, - abbottfreestyleneo: { - instructions: i18n.t('Plug in meter with micro-USB cable'), - key: 'abbottfreestyleneo', - name: 'Abbott FreeStyle Precision/Optium Neo', - source: {type: 'device', driverId: 'AbbottFreeStyleNeo'}, - enabled: {linux: true, mac: true, win: true}, - powerOnlyWarning: true, - }, - bayercontournext: { - instructions: i18n.t('Plug meter into USB port'), - key: 'bayercontournext', - name: 'Ascensia (Bayer) Contour Next', - source: {type: 'device', driverId: 'BayerContourNext'}, - enabled: {mac: true, win: true, linux: true} - }, - bayercontour: { - instructions: i18n.t('Plug in meter with cable and make sure meter is switched on'), - key: 'bayercontour', - name: 'Ascensia (Bayer) Contour Next EZ, Contour, Contour Link or Contour Plus', - source: {type: 'device', driverId: 'BayerContour'}, - enabled: {mac: true, win: true, linux: true} - }, - animas: { - instructions: i18n.t('Suspend and align back of pump with IR dongle front'), - key: 'animas', - name: 'Animas', - source: {type: 'device', driverId: 'Animas'}, - enabled: {mac: true, win: true, linux: true} - }, - onetouchverio: { - instructions: i18n.t('Plug in meter with micro-USB'), - name: 'OneTouch Verio, Verio Flex and Verio Reflect', - key: 'onetouchverio', - source: {type: 'device', driverId: 'OneTouchVerio'}, - enabled: {linux: true, mac: true, win: true}, - powerOnlyWarning: true, - }, - onetouchverioiq: { - instructions: i18n.t('Plug in meter with mini-USB'), - name: 'OneTouch VerioIQ', - key: 'onetouchverioiq', - source: {type: 'device', driverId: 'OneTouchVerioIQ'}, - enabled: {mac: true, win: true, linux: true}, - powerOnlyWarning: true, - }, - onetouchultramini: { - instructions: i18n.t('Plug in meter with cable and make sure the meter is switched off'), - name: 'OneTouch UltraMini', - key: 'onetouchultramini', - source: {type: 'device', driverId: 'OneTouchUltraMini'}, - enabled: {mac: true, win: true, linux: true} - }, - onetouchultra2: { - instructions: i18n.t('Plug in meter with cable and make sure the meter is switched off'), - name: 'OneTouch Ultra 2', - key: 'onetouchultra2', - source: {type: 'device', driverId: 'OneTouchUltra2'}, - enabled: {mac: true, win: true, linux: true} - }, - truemetrix: { - instructions: i18n.t('True Metrix & True Metrix Air: Place meter in cradle \u2022 True Metrix Go: Plug in meter with micro-USB cable'), - name: 'Trividia Health True Metrix', - key: 'truemetrix', - source: {type: 'device', driverId: 'TrueMetrix'}, - enabled: {mac: true, win: true, linux: true} - }, -}; - -export default devices; diff --git a/app/reducers/index.js b/app/reducers/index.js deleted file mode 100644 index cb275baac..000000000 --- a/app/reducers/index.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2015, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; -import { combineReducers } from 'redux'; -import { connectRouter } from 'connected-react-router'; -import { reducer as formReducer } from 'redux-form'; -import * as misc from './misc'; -import * as uploads from './uploads'; -import * as users from './users'; - -const rootReducer = function(history) { - return combineReducers( - _.assign( - misc, - uploads, - users, - { form: formReducer }, - { router: connectRouter(history) }, - ) - ); -}; - -export default rootReducer; diff --git a/app/reducers/misc.js b/app/reducers/misc.js deleted file mode 100644 index e32ebb889..000000000 --- a/app/reducers/misc.js +++ /dev/null @@ -1,309 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2015-2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; -import { combineReducers } from 'redux'; - -import * as actionTypes from '../constants/actionTypes'; -import { UnsupportedError } from '../utils/errors'; - -import initialDevices from './devices'; - -export function devices(state = initialDevices, action) { - switch (action.type) { - case actionTypes.HIDE_UNAVAILABLE_DEVICES: - function filterOutUnavailable(os) { - let filteredDevices = {}; - _.each(state, (device) => { - // if (device.enabled[os] === true) { - filteredDevices[device.key] = device; - // } - }); - return filteredDevices; - } - return filterOutUnavailable(action.payload.os); - default: - return state; - } -} - -export function dropdown(state = false, action) { - switch (action.type) { - case actionTypes.TOGGLE_DROPDOWN: - return action.payload.isVisible; - case actionTypes.LOGOUT_REQUEST: - return false; - default: - return state; - } -} - -export function os(state = null, action) { - switch (action.type) { - case actionTypes.SET_OS: - return action.payload.os; - default: - return state; - } -} - -export function unsupported(state = true, action) { - switch (action.type) { - case actionTypes.INIT_APP_FAILURE: - case actionTypes.VERSION_CHECK_FAILURE: - const err = action.payload; - if (err instanceof UnsupportedError) { - return true; - } - else { - return err; - } - case actionTypes.VERSION_CHECK_SUCCESS: - return false; - default: - return state; - } -} - -export function blipUrls(state = {}, action) { - switch (action.type) { - case actionTypes. SET_BLIP_VIEW_DATA_URL: - return _.assign({}, state, { - viewDataLink: action.payload.url - }); - case actionTypes.SET_FORGOT_PASSWORD_URL: - return _.assign({}, state, { - forgotPassword: action.payload.url - }); - case actionTypes.SET_SIGNUP_URL: - return _.assign({}, state, { - signUp: action.payload.url - }); - case actionTypes.SET_NEW_PATIENT_URL: - return _.assign({}, state, { - newPatient: action.payload.url - }); - default: - return state; - } -} - -function checkingVersion(state = false, action) { - switch (action.type) { - case actionTypes.VERSION_CHECK_FAILURE: - case actionTypes.VERSION_CHECK_SUCCESS: - return false; - case actionTypes.VERSION_CHECK_REQUEST: - return true; - default: - return state; - } -} - -function fetchingUserInfo(state = false, action) { - switch (action.type) { - case actionTypes.LOGIN_FAILURE: - case actionTypes.LOGIN_SUCCESS: - return false; - case actionTypes.LOGIN_REQUEST: - return true; - default: - return state; - } -} - -function initializingApp(state = true, action) { - switch (action.type) { - case actionTypes.INIT_APP_FAILURE: - case actionTypes.INIT_APP_SUCCESS: - return false; - case actionTypes.INIT_APP_REQUEST: - return true; - default: - return state; - } -} - -function uploading(state = false, action) { - switch (action.type) { - case actionTypes.UPLOAD_REQUEST: - return true; - case actionTypes.READ_FILE_ABORTED: - case actionTypes.READ_FILE_FAILURE: - case actionTypes.UPLOAD_FAILURE: - case actionTypes.UPLOAD_SUCCESS: - case actionTypes.UPLOAD_CANCELLED: - return false; - default: - return state; - } -} - -function checkingElectronUpdate(state = false, action) { - switch (action.type) { - case actionTypes.CHECKING_FOR_UPDATES: - case actionTypes.AUTO_UPDATE_CHECKING_FOR_UPDATES: - case actionTypes.MANUAL_UPDATE_CHECKING_FOR_UPDATES: - return true; - case actionTypes.UPDATE_AVAILABLE: - case actionTypes.UPDATE_NOT_AVAILABLE: - case actionTypes.AUTOUPDATE_ERROR: - return false; - default: - return state; - } -} - -function checkingDriverUpdate(state = false, action) { - switch (action.type) { - case actionTypes.CHECKING_FOR_DRIVER_UPDATE: - return true; - case actionTypes.DRIVER_UPDATE_AVAILABLE: - case actionTypes.DRIVER_UPDATE_NOT_AVAILABLE: - return false; - default: - return state; - } -} - -export const working = combineReducers({ - checkingVersion, fetchingUserInfo, initializingApp, uploading, checkingElectronUpdate, checkingDriverUpdate -}); - -export function electronUpdateManualChecked(state = null, action) { - switch (action.type) { - case actionTypes.MANUAL_UPDATE_CHECKING_FOR_UPDATES: - return true; - case actionTypes.DISMISS_UPDATE_NOT_AVAILABLE: - return null; - default: - return state; - } -} - -export function electronUpdateAvailableDismissed(state = null, action) { - switch (action.type) { - case actionTypes.MANUAL_UPDATE_CHECKING_FOR_UPDATES: - return null; - case actionTypes.DISMISS_UPDATE_AVAILABLE: - return true; - default: - return state; - } -} - -export function electronUpdateAvailable(state = null, action) { - switch (action.type) { - case actionTypes.AUTO_UPDATE_CHECKING_FOR_UPDATES: - case actionTypes.MANUAL_UPDATE_CHECKING_FOR_UPDATES: - return null; - case actionTypes.UPDATE_AVAILABLE: - return true; - case actionTypes.UPDATE_NOT_AVAILABLE: - return false; - default: - return state; - } -} - -export function electronUpdateDownloaded(state = null, action) { - switch (action.type) { - case actionTypes.UPDATE_AVAILABLE: - return null; - case actionTypes.UPDATE_DOWNLOADED: - return true; - case actionTypes.AUTOUPDATE_ERROR: - return false; - default: - return state; - } -} - -export function driverUpdateAvailable(state = null, action) { - switch (action.type) { - case actionTypes.DRIVER_UPDATE_AVAILABLE: - return action.payload; - case actionTypes.DRIVER_UPDATE_NOT_AVAILABLE: - case actionTypes.DRIVER_INSTALL: - return false; - default: - return state; - } -} - -export function driverUpdateAvailableDismissed(state = null, action) { - switch (action.type) { - case actionTypes.CHECKING_FOR_DRIVER_UPDATE: - return false; - case actionTypes.DISMISS_DRIVER_UPDATE_AVAILABLE: - return true; - default: - return state; - } -} - -export function driverUpdateShellOpts(state = null, action) { - switch (action.type) { - case actionTypes.DRIVER_INSTALL_SHELL_OPTS: - return action.payload; - default: - return state; - } -} - -export function driverUpdateComplete(state = null, action) { - switch (action.type) { - case actionTypes.DRIVER_INSTALL: - return true; - default: - return state; - } -} - -export function showingDeviceTimePrompt(state = null, action) { - switch (action.type) { - case actionTypes.DEVICE_TIME_INCORRECT: - return { callback: action.payload.callback, cfg: action.payload.cfg, times: action.payload.times }; - case actionTypes.DISMISS_DEVICE_TIME_PROMPT: - return false; - default: - return state; - } -} - -export function isTimezoneFocused(state = false, action) { - switch (action.type) { - case actionTypes.UPLOAD_CANCELLED: - return true; - case actionTypes.TIMEZONE_BLUR: - case actionTypes.UPLOAD_REQUEST: - return false; - default: - return state; - } -} - -export function showingAdHocPairingDialog(state = false, action) { - switch (action.type) { - case actionTypes.AD_HOC_PAIRING_REQUEST: - return { callback: action.payload.callback, cfg: action.payload.cfg }; - case actionTypes.AD_HOC_PAIRING_DISMISSED: - return false; - default: - return state; - } -} diff --git a/app/reducers/uploads.js b/app/reducers/uploads.js deleted file mode 100644 index 9dd3060b9..000000000 --- a/app/reducers/uploads.js +++ /dev/null @@ -1,533 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; -import update from 'immutability-helper'; - -import * as actionTypes from '../constants/actionTypes'; -import { steps } from '../constants/otherConstants'; - -function isPwd(membership) { - return !_.isEmpty(_.get(membership, ['profile', 'patient'], {})); -} - -export function uploadProgress(state = null, action) { - switch (action.type) { - case actionTypes.CARELINK_FETCH_REQUEST: - return { - percentage: 0, - step: steps.carelinkFetch - }; - case actionTypes.DEVICE_DETECT_REQUEST: - return { - percentage: 0, - step: steps.detect - }; - case actionTypes.UPLOAD_FAILURE: - case actionTypes.UPLOAD_SUCCESS: - case actionTypes.UPLOAD_CANCELLED: - return null; - case actionTypes.UPLOAD_PROGRESS: - return Object.assign({}, state, action.payload); - case actionTypes.UPLOAD_REQUEST: - return { - percentage: 0, - step: steps.start - }; - default: - return state; - } -} - -export function uploadsByUser(state = {}, action) { - switch (action.type) { - case actionTypes.CARELINK_FETCH_FAILURE: - let uploadTargetUser; - const uploadTargetDevice = 'carelink'; - _.forOwn(state, (uploads, userId) => { - _.forOwn(uploads, (upload, deviceKey) => { - if (deviceKey === 'carelink' && upload.isFetching === true) { - uploadTargetUser = userId; - } - }); - }); - if (uploadTargetUser) { - return update( - state, - {[uploadTargetUser]: {[uploadTargetDevice]: { - isFetching: {$set: false} - }}} - ); - } - case actionTypes.CARELINK_FETCH_REQUEST: { - const { userId, deviceKey } = action.payload; - return update( - state, - {[userId]: {[deviceKey]: {isFetching: {$set: true}}}} - ); - } - case actionTypes.CARELINK_FETCH_SUCCESS: { - const { userId, deviceKey } = action.payload; - return update( - state, - {[userId]: {[deviceKey]: {isFetching: {$set: false}}}} - ); - } - case actionTypes.CHOOSING_FILE: { - const { userId, deviceKey } = action.payload; - let newState = state; - let devicesForCurrentUser = _.get(state, [userId], {}); - _.forOwn(devicesForCurrentUser, (upload, key) => { - newState = update( - newState, - {[userId]: {[key]: {$apply: (upload) => { - if (key === deviceKey) { - return update( - upload, - { - choosingFile: {$set: true} - } - ); - } - else { - return update( - upload, - {disabled: {$set: true}} - ); - } - }}}} - ); - }); - return newState; - } - case actionTypes.READ_FILE_ABORTED: { - const err = action.payload; - let uploadTargetUser, uploadTargetDevice; - let newState = state; - _.forOwn(state, (uploads, userId) => { - _.forOwn(uploads, (upload, deviceKey) => { - if (upload.choosingFile === true) { - uploadTargetUser = userId; - uploadTargetDevice = deviceKey; - } - }); - }); - if (uploadTargetUser && uploadTargetDevice) { - let devicesForCurrentUser = _.get(state, uploadTargetUser, {}); - _.forOwn(devicesForCurrentUser, (upload, key) => { - newState = update( - newState, - {[uploadTargetUser]: {[key]: {$apply: (upload) => { - if (key === uploadTargetDevice) { - return update( - upload, - { - choosingFile: {$set: false}, - completed: {$set: true}, - error: {$set: err}, - failed: {$set: true} - } - ); - } - else { - return update( - upload, - {disabled: {$set: false}} - ); - } - }}}} - ); - }); - } - return newState; - } - case actionTypes.READ_FILE_FAILURE: { - const err = action.payload; - let uploadTargetUser, uploadTargetDevice; - _.forOwn(state, (uploads, userId) => { - _.forOwn(uploads, (upload, deviceKey) => { - if (upload.readingFile === true) { - uploadTargetUser = userId; - uploadTargetDevice = deviceKey; - } - }); - }); - if (uploadTargetUser && uploadTargetDevice) { - return update( - state, - {[uploadTargetUser]: {[uploadTargetDevice]: { - completed: {$set: true}, - error: {$set: err}, - failed: {$set: true}, - readingFile: {$set: false} - }}} - ); - } - } - case actionTypes.READ_FILE_REQUEST: { - const { userId, deviceKey, filename } = action.payload; - return update( - state, - {[userId]: {[deviceKey]: { - choosingFile: {$set: false}, - file: {$set: {name: filename}}, - readingFile: {$set: true} - }}} - ); - } - case actionTypes.READ_FILE_SUCCESS: { - const { userId, deviceKey, filedata } = action.payload; - return update( - state, - {[userId]: {[deviceKey]: { - file: {data: {$set: filedata}}, - readingFile: {$set: false} - }}} - ); - } - case actionTypes.RESET_UPLOAD: { - const { userId, deviceKey } = action.payload; - const uploadInProgress = _.some( - _.get(state, [userId], {}), - (upload, key) => { - const fileDataExists = _.get(upload, ['file', 'data'], null) !== null; - // because we don't want the existence of file.data on a block-mode device - // to make it appear as though an upload is in progress when we're trying - // to reset the block-mode device itself! - if (key !== deviceKey) { - return upload.choosingFile || - upload.readingFile || - (fileDataExists && !upload.completed) || - upload.uploading; - } - else { - return false; - } - } - ); - if (uploadInProgress) { - return update( - state, - {[userId]: {[deviceKey]: {$apply: (upload) => { - let resetUpload = _.pick(upload, 'history'); - resetUpload.disabled = true; - return resetUpload; - }}}} - ); - } - else { - return update( - state, - {[userId]: {[deviceKey]: {$apply: (upload) => { - return _.pick(upload, 'history'); - }}}} - ); - } - } - case actionTypes.SET_UPLOADS: - const { devicesByUser } = action.payload; - let newState = state; - _.forOwn(devicesByUser, (deviceKeys, userId) => { - if (_.get(newState, userId, null) === null) { - const uploadsForUser = {}; - _.each(deviceKeys, (deviceKey) => { - uploadsForUser[deviceKey] = {history: []}; - }); - newState = update( - newState, - {[userId]: {$set: uploadsForUser}} - ); - } - else { - _.each(deviceKeys, (deviceKey) => { - if (_.get(newState, [userId, deviceKey], null) === null) { - newState = update( - newState, - {[userId]: {[deviceKey]: {$set: {history: []}}}} - ); - } - }); - const devicesToDelete = _.difference( - Object.keys(newState[userId]), - deviceKeys - ); - if (!_.isEmpty(devicesToDelete)) { - newState = update( - newState, - {[userId]: {$apply: (uploadsForUser) => { - _.each(devicesToDelete, (deviceKey) => { - uploadsForUser = _.omit(uploadsForUser, deviceKey); - }); - return uploadsForUser; - }}} - ); - } - } - }); - return newState; - case actionTypes.TOGGLE_ERROR_DETAILS: { - const { userId, deviceKey, isVisible } = action.payload; - return update( - state, - {[userId]: {[deviceKey]: {showErrorDetails: {$set: isVisible}}}} - ); - } - case actionTypes.UPLOAD_CANCELLED: { - const { utc } = action.payload; - let uploadTargetUser, uploadTargetDevice; - _.forOwn(state, (uploads, userId) => { - _.forOwn(uploads, (upload, deviceKey) => { - if (upload.uploading === true) { - uploadTargetUser = userId; - uploadTargetDevice = deviceKey; - } - }); - }); - if (uploadTargetUser && uploadTargetDevice) { - let newState = state; - let devicesForCurrentUser = _.get(state, [uploadTargetUser], {}); - _.forOwn(devicesForCurrentUser, (upload, key) => { - newState = update( - newState, - {[uploadTargetUser]: {[key]: {$apply: (upload) => { - if (key === uploadTargetDevice) { - return update( - upload, - { - completed: {$set: true}, - failed: {$set: false}, - history: {[0]: { - finish: {$set: utc} - }}, - uploading: {$set: false} - } - ); - } - else { - return _.omit(upload, 'disabled'); - } - }}}} - ); - }); - return newState; - } - } - case actionTypes.UPLOAD_FAILURE: { - const err = action.payload; - let uploadTargetUser, uploadTargetDevice; - _.forOwn(state, (uploads, userId) => { - _.forOwn(uploads, (upload, deviceKey) => { - if (upload.uploading === true) { - uploadTargetUser = userId; - uploadTargetDevice = deviceKey; - } - }); - }); - if (uploadTargetUser && uploadTargetDevice) { - let newState = state; - let devicesForCurrentUser = _.get(state, [uploadTargetUser], {}); - _.forOwn(devicesForCurrentUser, (upload, key) => { - newState = update( - newState, - {[uploadTargetUser]: {[key]: {$apply: (upload) => { - if (key === uploadTargetDevice) { - return update( - upload, - { - completed: {$set: true}, - error: {$set: err}, - failed: {$set: true}, - history: {[0]: { - error: {$set: true}, - finish: {$set: err.utc} - }}, - uploading: {$set: false} - } - ); - } - else { - return _.omit(upload, 'disabled'); - } - }}}} - ); - }); - return newState; - } - } - case actionTypes.UPLOAD_REQUEST: { - const { userId, deviceKey, utc } = action.payload; - let newState = state; - let devicesForCurrentUser = _.get(state, [userId], {}); - _.forOwn(devicesForCurrentUser, (upload, key) => { - newState = update( - newState, - {[userId]: {[key]: {$apply: (upload) => { - if (key === deviceKey) { - return update( - upload, - { - history: {$unshift: [{start: utc}]}, - uploading: {$set: true} - } - ); - } - else { - return update( - upload, - {disabled: {$set: true}} - ); - } - }}}} - ); - }); - return newState; - } - case actionTypes.UPLOAD_SUCCESS: { - const { userId, deviceKey, data, utc } = action.payload; - let newState = state; - let devicesForCurrentUser = _.get(state, [userId], {}); - _.forOwn(devicesForCurrentUser, (upload, key) => { - newState = update( - newState, - {[userId]: {[key]: {$apply: (upload) => { - if (key === deviceKey) { - return update( - upload, - { - completed: {$set: true}, - data: {$set: data}, - history: {[0]: { - finish: {$set: utc} - }}, - successful: {$set: true}, - uploading: {$set: false} - } - ); - } - else { - return _.omit(upload, 'disabled'); - } - }}}} - ); - }); - return newState; - } - case actionTypes.ADD_TARGET_DEVICE: { - const { userId, deviceKey } = action.payload; - let newState = state; - if (_.get(newState, userId, null) === null) { - const uploadsForUser = {}; - uploadsForUser[deviceKey] = {history: []}; - newState = update( - newState, - {[userId]: {$set: uploadsForUser}} - ); - } - else { - if (_.get(newState, [userId, deviceKey], null) === null) { - newState = update( - newState, - {[userId]: {[deviceKey]: {$set: {history: []}}}} - ); - } - } - return newState; - } - case actionTypes.REMOVE_TARGET_DEVICE: { - const { userId, deviceKey } = action.payload; - let newState = state; - if (_.get(newState, [userId, deviceKey], null) !== null) { - newState = update( - newState, - {[userId]: {$apply: (uploadsForUser) => { - uploadsForUser = _.omit(uploadsForUser, deviceKey); - return uploadsForUser; - }}} - ); - } - return newState; - } - case actionTypes.LOGIN_SUCCESS: - case actionTypes.SET_USER_INFO_FROM_TOKEN: { - const { memberships } = action.payload; - let newState = state; - _.each(memberships, (membership) => { - if (isPwd(membership)) { - const deviceKeys = _.get(membership, ['profile', 'patient', 'targetDevices'], null); - if(deviceKeys !== null){ - const userId = membership.userid; - if (_.get(newState, userId, null) === null) { - const uploadsForUser = {}; - _.each(deviceKeys, (deviceKey) => { - uploadsForUser[deviceKey] = {history: []}; - }); - newState = update( - newState, - {[userId]: {$set: uploadsForUser}} - ); - } - else { - _.each(deviceKeys, (deviceKey) => { - if (_.get(newState, [userId, deviceKey], null) === null) { - newState = update( - newState, - {[userId]: {[deviceKey]: {$set: {history: []}}}} - ); - } - }); - const devicesToDelete = _.difference( - Object.keys(newState[userId]), - deviceKeys - ); - if (!_.isEmpty(devicesToDelete)) { - newState = update( - newState, - {[userId]: {$apply: (uploadsForUser) => { - _.each(devicesToDelete, (deviceKey) => { - uploadsForUser = _.omit(uploadsForUser, deviceKey); - }); - return uploadsForUser; - }}} - ); - } - } - } - } - }); - return newState; - } - default: - return state; - } -} - -export function uploadTargetDevice(state = null, action) { - switch (action.type) { - case actionTypes.CHOOSING_FILE: - case actionTypes.UPLOAD_REQUEST: { - const { deviceKey } = action.payload; - return deviceKey; - } - case actionTypes.READ_FILE_ABORTED: - case actionTypes.READ_FILE_FAILURE: - case actionTypes.UPLOAD_FAILURE: - case actionTypes.UPLOAD_SUCCESS: - return null; - default: - return state; - } -} diff --git a/app/reducers/users.js b/app/reducers/users.js deleted file mode 100644 index 6579653cf..000000000 --- a/app/reducers/users.js +++ /dev/null @@ -1,352 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; -import update from 'immutability-helper'; -import personUtils from '../../lib/core/personUtils'; - -import * as actionTypes from '../constants/actionTypes'; - -export function allUsers(state = {}, action) { - switch (action.type) { - case actionTypes.LOGIN_SUCCESS: - case actionTypes.SET_USER_INFO_FROM_TOKEN: - case actionTypes.SET_ALL_USERS: { - const { user, profile, memberships } = action.payload; - let newState = {}; - _.each(memberships, (membership) => { - newState[membership.userid] = (membership.userid === user.userid) ? - _.assign({}, _.omit(user, 'userid'), profile) : - _.assign({}, membership.profile); - }); - return newState; - } - case actionTypes.CREATE_CUSTODIAL_ACCOUNT_SUCCESS: - const { account } = action.payload; - return update(state, {$merge: {[account.userid]: account.profile}}); - case actionTypes.UPDATE_PROFILE_SUCCESS: - const { userId, profile } = action.payload; - return update(state, {$merge: {[userId]: profile}}); - case actionTypes.LOGOUT_REQUEST: - return {}; - default: - return state; - } -} - -export function memberships(state = {}, action) { - switch (action.type) { - case actionTypes.LOGIN_SUCCESS: - case actionTypes.SET_USER_INFO_FROM_TOKEN: - case actionTypes.SET_ALL_USERS: { - const { memberships } = action.payload; - let newState = {}; - _.each(memberships, (membership) => { - newState[membership.userid] = _.assign({}, _.omit(membership, ['userid', 'profile'])); - }); - return newState; - } - case actionTypes.CREATE_CUSTODIAL_ACCOUNT_SUCCESS: - const { account } = action.payload; - return update(state, { $merge: { [account.userid]: { permissions: { custodian: {}, upload: {}, view: {} } } } }); - case actionTypes.LOGOUT_REQUEST: - return {}; - default: - return state; - } -} - -export function loggedInUser(state = null, action) { - switch (action.type) { - case actionTypes.LOGIN_SUCCESS: - case actionTypes.SET_USER_INFO_FROM_TOKEN: - const { user } = action.payload; - return user.userid; - case actionTypes.LOGOUT_REQUEST: - return null; - default: - return state; - } -} - -export function loginErrorMessage(state = null, action) { - switch (action.type) { - case actionTypes.LOGIN_FAILURE: - const err = action.payload; - return err.message; - case actionTypes.LOGIN_REQUEST: - return null; - default: - return state; - } -} - -export function updateProfileErrorMessage(state = null, action) { - switch (action.type) { - case actionTypes.UPDATE_PROFILE_FAILURE: - const err = action.payload; - return err.message; - case actionTypes.UPDATE_PROFILE_REQUEST: - case actionTypes.SET_UPLOAD_TARGET_USER: - return null; - default: - return state; - } -} - -export function updateProfileErrorDismissed(state = null, action) { - switch (action.type) { - case actionTypes.UPDATE_PROFILE_REQUEST: - case actionTypes.SET_UPLOAD_TARGET_USER: - return null; - case actionTypes.DISMISS_UPDATE_PROFILE_ERROR: - return true; - default: - return state; - } -} - -export function createCustodialAccountErrorMessage(state = null, action) { - switch (action.type) { - case actionTypes.CREATE_CUSTODIAL_ACCOUNT_FAILURE: - const err = action.payload; - return err.message; - case actionTypes.CREATE_CUSTODIAL_ACCOUNT_REQUEST: - return null; - default: - return state; - } -} - -export function createCustodialAccountErrorDismissed(state = false, action) { - switch (action.type) { - case actionTypes.CREATE_CUSTODIAL_ACCOUNT_REQUEST: - return false; - case actionTypes.DISMISS_CREATE_CUSTODIAL_ACCOUNT_ERROR: - return true; - default: - return state; - } -} - -function isPwd(membership) { - return !_.isEmpty(_.get(membership, ['profile', 'patient'], {})); -} - -function isVCA(membership) { - return personUtils.userHasRole(membership, 'clinic'); -} - -export function targetDevices(state = {}, action) { - switch (action.type) { - case actionTypes.ADD_TARGET_DEVICE: { - const { userId, deviceKey } = action.payload; - return update( - state, - {[userId]: {$apply: (devicesArray) => { - if (devicesArray == null) { - return [deviceKey]; - } - else if (!_.includes(devicesArray, deviceKey)) { - let newDevices = devicesArray.slice(0); - newDevices.push(deviceKey); - return newDevices; - } - else { - return devicesArray; - } - }}} - ); - } - // create some scaffolding based on the users the loggedInUser - // currently has upload access to - case actionTypes.LOGIN_SUCCESS: - case actionTypes.SET_USER_INFO_FROM_TOKEN: { - const { memberships } = action.payload; - let newState = {}; - _.each(memberships, (membership) => { - if (isPwd(membership)) { - let targetDevices = _.get(membership, ['profile', 'patient', 'targetDevices'], []); - // collapse all bayercontour* devices into bayercontournext - targetDevices = _.uniq(_.map(targetDevices, function(device) { - if (device.startsWith('bayercontour') && device.length > 12) { - return 'bayercontournext'; - } - if (device === 'abbottfreestylefreedomlite') { - return 'abbottfreestylelite'; - } - return device; - })); - newState[membership.userid] = targetDevices; - } - }); - return newState; - } - case actionTypes.LOGOUT_REQUEST: - return {}; - case actionTypes.REMOVE_TARGET_DEVICE: { - const { userId, deviceKey } = action.payload; - return update( - state, - {[userId]: {$apply: (devices) => { - return _.filter(devices, (device) => { - return device !== deviceKey; - }); - }}} - ); - } - case actionTypes.SET_USERS_TARGETS: { - const { targets } = action.payload; - let newState = state; - _.forOwn(targets, (targetsArray, userId) => { - if (newState[userId] != null) { - let targetDevices = _.map(targetsArray, 'key'); - // collapse all bayercontour* devices into bayercontournext - targetDevices = _.uniq(_.map(targetDevices, function(device) { - if (device.startsWith('bayercontour') && device.length > 12) { - return 'bayercontournext'; - } - if (device === 'abbottfreestylefreedomlite') { - return 'abbottfreestylelite'; - } - return device; - })); - newState = update( - newState, - {[userId]: {$set: targetDevices}} - ); - } - }); - return newState; - } - case actionTypes.STORING_USERS_TARGETS: - // _.omit returns a new object, doesn't mutate - return _.omit(state, 'noUserSelected'); - - default: - return state; - } -} - -export function targetTimezones(state = {}, action) { - switch (action.type) { - case actionTypes.LOGIN_SUCCESS: - case actionTypes.SET_USER_INFO_FROM_TOKEN: - const { memberships } = action.payload; - let newState = {}; - _.each(memberships, (membership) => { - if (isPwd(membership)) { - newState[membership.userid] = _.get(membership, ['profile', 'patient', 'targetTimezone'], null); - } - }); - return newState; - case actionTypes.LOGOUT_REQUEST: - return {}; - case actionTypes.SET_TARGET_TIMEZONE: { - const { userId, timezoneName } = action.payload; - return update( - state, - {[userId]: {$set: timezoneName}} - ); - } - case actionTypes.SET_USERS_TARGETS: { - const { targets } = action.payload; - let newState = state; - _.forOwn(targets, (targetsArray, userId) => { - // we have to check *specifically* for undefined here - // because we use null when there isn't a timezone - if (newState[userId] !== undefined) { - const targetTimezones = _.uniq(_.map(targetsArray, 'timezone')); - if (targetTimezones.length === 1) { - newState = update( - newState, - {[userId]: {$set: targetTimezones[0]}} - ); - } - // if different timezones are stored for different devices - // we set to `null` to force the user to choose again - else { - newState = update( - newState, - {[userId]: {$set: null}} - ); - } - } - }); - return newState; - } - case actionTypes.STORING_USERS_TARGETS: - // _.omit returns a new object, doesn't mutate - return _.omit(state, 'noUserSelected'); - default: - return state; - } -} - -export function targetUsersForUpload(state = [], action) { - switch (action.type) { - case actionTypes.LOGIN_SUCCESS: - case actionTypes.SET_USER_INFO_FROM_TOKEN: - case actionTypes.SET_ALL_USERS: - const { user, profile, memberships } = action.payload; - let newState = []; - _.each(memberships, (membership) => { - if (membership.userid === user.userid) { - if (!isVCA(user) && !_.isEmpty(profile.patient)){ - newState.push(membership.userid); - } - } else { - newState.push(membership.userid); - } - }); - return newState; - case actionTypes.CREATE_CUSTODIAL_ACCOUNT_SUCCESS: - const { account } = action.payload; - return update(state, {$push: [account.userid]}); - case actionTypes.LOGOUT_REQUEST: - return []; - default: - return state; - } -} - -export function uploadTargetUser(state = null, action) { - switch (action.type) { - case actionTypes.LOGIN_SUCCESS: - case actionTypes.SET_USER_INFO_FROM_TOKEN: - const { user, profile, memberships } = action.payload; - const uploadMemberships = _.filter(memberships, (mship) => { - return !_.isEmpty(_.get(mship, ['profile', 'patient'])); - }); - if (!_.isEmpty(profile.patient)) { - return user.userid; - } - else if (uploadMemberships.length === 1 && !isVCA(user)) { - return uploadMemberships[0].userid; - } - else { - return null; - } - case actionTypes.SET_UPLOAD_TARGET_USER: - const { userId } = action.payload; - return userId; - case actionTypes.LOGOUT_REQUEST: - return null; - default: - return state; - } -} diff --git a/app/store/configureStore.development.js b/app/store/configureStore.development.js deleted file mode 100644 index f31517940..000000000 --- a/app/store/configureStore.development.js +++ /dev/null @@ -1,57 +0,0 @@ -import { createStore, applyMiddleware, compose } from 'redux'; -import thunk from 'redux-thunk'; -import { routerMiddleware, push } from 'connected-react-router'; -import rootReducer from '../reducers'; -import { async, sync } from '../actions'; -import api from '../../lib/core/api'; -import config from '../../lib/config'; -import { createErrorLogger } from '../utils/errors'; -import { createMetricsTracker } from '../utils/metrics'; - -api.create({ - apiUrl: config.API_URL, - uploadUrl: config.UPLOAD_URL, - dataUrl: config.DATA_URL, - version: config.version -}); - -const actionCreators = { - ...async, - ...sync, - push, -}; - -export default function configureStore(initialState, history) { - const router = routerMiddleware(history); - - // If Redux DevTools Extension is installed use it, otherwise use Redux compose - /* eslint-disable no-underscore-dangle */ - const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? - window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ - // Options: http://zalmoxisus.github.io/redux-devtools-extension/API/Arguments.html - actionCreators, - }) : - compose; - /* eslint-enable no-underscore-dangle */ - - const enhancer = composeEnhancers( - applyMiddleware( - thunk, - router, - createErrorLogger(api), - createMetricsTracker(api) - ) - ); - - const store = createStore(rootReducer(history), initialState, enhancer); - - // if (module.hot) { - // module.hot.accept('../reducers', () => - // store.replaceReducer(require('../reducers')(history)).default // eslint-disable-line global-require - // ); - // } - - return store; -} - -export { api }; diff --git a/app/store/configureStore.js b/app/store/configureStore.js deleted file mode 100755 index eca511fcc..000000000 --- a/app/store/configureStore.js +++ /dev/null @@ -1,5 +0,0 @@ -if (process.env.NODE_ENV === 'production') { - module.exports = require('./configureStore.production'); // eslint-disable-line global-require -} else { - module.exports = require('./configureStore.development'); // eslint-disable-line global-require -} diff --git a/app/store/configureStore.production.js b/app/store/configureStore.production.js deleted file mode 100644 index 88f28a946..000000000 --- a/app/store/configureStore.production.js +++ /dev/null @@ -1,27 +0,0 @@ -import { createStore, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; -import { routerMiddleware } from 'connected-react-router'; -import rootReducer from '../reducers'; -import api from '../../lib/core/api'; -import config from '../../lib/config'; -import { createErrorLogger } from '../utils/errors'; -import { createMetricsTracker } from '../utils/metrics'; - -api.create({ - apiUrl: config.API_URL, - uploadUrl: config.UPLOAD_URL, - dataUrl: config.DATA_URL, - version: config.version -}); - -export default function configureStore(initialState, history) { - const router = routerMiddleware(history); - const enhancer = applyMiddleware( - thunk, - router, - createErrorLogger(api), - createMetricsTracker(api) - ); - - return createStore(rootReducer(history), initialState, enhancer); // eslint-disable-line -} diff --git a/app/utils/config.i18next.js b/app/utils/config.i18next.js deleted file mode 100644 index 992fc655b..000000000 --- a/app/utils/config.i18next.js +++ /dev/null @@ -1,21 +0,0 @@ -var path = require('path'); - -let dirPath = (process.env.NODE_ENV === 'production') ? path.join(__dirname, '../') : '.'; -let i18nextOptions = module.exports = { - backend: { - loadPath: dirPath + '/locales/{{lng}}/{{ns}}.json', - addPath: dirPath + '/locales/{{lng}}/{{ns}}.missing.json' - }, - interpolation: { - escapeValue: false - }, - lng: 'en', - saveMissing: true, - fallbackLng: 'en', - returnEmptyString: false, - whitelist: ['en', 'es'], - keySeparator: false, - nsSeparator: '|', - debug: false, - wait: true -}; diff --git a/app/utils/drivers.darwin.js b/app/utils/drivers.darwin.js deleted file mode 100644 index 96717f423..000000000 --- a/app/utils/drivers.darwin.js +++ /dev/null @@ -1,90 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2017, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import { app } from '@electron/remote'; -import plist from 'plist'; -import fs from 'fs'; -import path from 'path'; -import isDev from 'electron-is-dev'; -import * as sync from '../actions/sync'; - -export function checkVersion(dispatch) { - - dispatch(sync.checkingForDriverUpdate()); - - function setInstallOpts(iconsPath, scriptPath, driverPath) { - const options = { - name: 'Tidepool Driver Installer', - icns: iconsPath - }; - const execString = scriptPath.replace(/ /g, '\\ ') + ' ' + driverPath.replace(/ /g, '\\ '); - dispatch(sync.driverUpdateShellOpts({options,execString})); - } - - function readVersion(pListFile) { - try { - const list = plist.parse(fs.readFileSync(pListFile, 'utf8')); - return list.CFBundleVersion; - } catch (error) { - if (error.code === 'ENOENT') { - return 'Not found'; - } else { - console.log(error); - } - return null; - } - } - - function hasOldDriver(dPath, driverList, installPath, pListFile) { - let installedVersion, currentVersion; - for (const driver of driverList) { - if (pListFile === null) { - currentVersion = readVersion(path.join(dPath, driver, driver + '.plist')); - installedVersion = readVersion(path.join(installPath, driver + '.plist')); - } else { - currentVersion = readVersion(path.join(dPath, driver, pListFile)); - installedVersion = readVersion(path.join(installPath, driver, pListFile)); - } - console.log(driver,'version: Installed =', installedVersion, ', Current =', currentVersion); - - if(currentVersion !== installedVersion) { - dispatch(sync.driverUpdateAvailable(installedVersion, currentVersion)); - return true; - } - } - dispatch(sync.driverUpdateNotAvailable(installedVersion)); - return false; - } - - const appFolder = path.dirname(app.getAppPath()); - let helperPath = path.join(appFolder, 'driver/helpers/'); - let driverPath = path.join(appFolder, 'driver/'); - let iconsPath = path.join(appFolder, '/Tidepool Uploader.icns'); - let scriptPath = path.join(appFolder, 'driver/updateDrivers.sh'); - - if (isDev) { - driverPath = path.resolve(appFolder, 'build/driver/'); - helperPath = path.join(appFolder, 'resources/mac/helpers/'); - iconsPath = path.join(appFolder, 'resources/icon.icns'); - scriptPath = path.resolve(appFolder, 'resources/mac/updateDrivers.sh'); - } - - const helperList = fs.readdirSync(helperPath).filter(e => e[0] !== '.'); - if (hasOldDriver(helperPath, helperList, '/Library/LaunchDaemons/', null)) { - setInstallOpts(iconsPath, scriptPath, driverPath); - } -} diff --git a/app/utils/drivers.js b/app/utils/drivers.js deleted file mode 100644 index 05135dcd7..000000000 --- a/app/utils/drivers.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2015-2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import os from 'os'; - -const platform = os.platform(); -if (platform === 'darwin'){ - module.exports = require('./drivers.darwin'); // eslint-disable-line global-require -} else { - module.exports = require('./drivers.win32'); // eslint-disable-line global-require -} diff --git a/app/utils/drivers.win32.js b/app/utils/drivers.win32.js deleted file mode 100644 index 9284014d3..000000000 --- a/app/utils/drivers.win32.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2015-2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -// TODO: pick one of the two options below after testing thoroughly - -import _ from 'lodash'; -//import regedit from 'regedit'; -import winreg from 'winreg'; - -export function checkVersion(dispatch) { -/* - var software; - regedit.list('HKLM\\SYSTEM\\DriverDatabase\\DriverPackages', - function(err, result) { - software = result['HKLM\\SYSTEM\\DriverDatabase\\DriverPackages'].keys; - var filtered = _.filter(software, function(name){ - return _.startsWith(name, 'tidepool'); - }); - var tidepoolPaths = _.map(filtered, function(key) { return 'HKLM\\SYSTEM\\DriverDatabase\\DriverPackages\\' + key; }); - regedit.list(tidepoolPaths, function(err, result) { - _.forEach(result, function(regvalues){ - var versionValue = regvalues.values.Version.value; - console.log([versionValue[38],versionValue[36],versionValue[34],versionValue[32]].join('.')); - }); - }); - } - ); -*/ - var regKey = winreg({ - hive: winreg.HKLM, - key: '\\SYSTEM\\DriverDatabase\\DriverPackages' - }); - - regKey.keys(function(err, items){ - var filtered = _.filter(items, function(item){ - return _.startsWith(_.split(item.key, '\\').pop(), 'tidepool'); - }); - _.each(filtered, function(tidepoolKey){ - console.log(tidepoolKey.key); - tidepoolKey.values(function(err, values){ - _.each(values, function(value){ - if(value.name === 'Version'){ - console.log('Driver version: ', [value.value[77], value.value[73], value.value[69], value.value[65]].join('.')); - } - }); - }); - }); - }); -} diff --git a/app/utils/errors.js b/app/utils/errors.js deleted file mode 100644 index dc52b7a2c..000000000 --- a/app/utils/errors.js +++ /dev/null @@ -1,127 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2015-2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; - -import errorText from '../constants/errors'; - -const errorProps = { - code: 'Code', - details: 'Details', - name: 'Name', - step: 'Driver Step', - datasetId: 'Dataset ID', - requestTrace: 'Request Trace', - sessionToken: 'Session Token', - sessionTrace: 'Session Trace', - stringifiedStack: 'Stack Trace', - utc: 'UTC Time', - version: 'Version' -}; - -export function addInfoToError(err, props) { - let debug = []; - _.forOwn(props, (v, k) => { - if (!_.isEmpty(v) && v !== err.message) { - err[k] = v; - debug.push(`${errorProps[k]}: ${v}`); - } - }); - if (!_.isEmpty(debug)) { - err.debug = debug.join(' | '); - } - return err; -} - -export function getAppInitErrorMessage(status) { - switch(status) { - case 503: - return errorText.E_OFFLINE; - default: - return errorText.E_INIT; - } -} - -export function getLoginErrorMessage(status) { - switch(status) { - case 400: - return 'We need your e-mail to log you in!'; - case 401: - return 'Please check your e-mail and password.'; - default: - return 'We couldn\'t log you in. Try again in a few minutes.'; - } -} - -export function getUpdateProfileErrorMessage(status) { - switch (status) { - case 400: - return 'Something looks funky, make sure this account info is correct.'; - case 401: - return 'You need to be logged in to update your preferences.'; - case 409: - return 'This email is already associated with a Tidepool account.'; - case 503: - return errorText.E_OFFLINE; - default: - return 'We can\'t save your device and timezone selection right now.'; - } -} - -export function getCreateCustodialAccountErrorMessage(status){ - switch(status) { - case 400: - return 'Something looks funky, make sure this account info is correct.'; - case 401: - return 'Your session timed out. You\'ll need to log back in.'; - case 409: - return 'We can\'t create this account because that email address already has an account.'; - case 500: - return 'Er sorry, we can\'t create this account right now. Try again in a few minutes. '; - default: - return 'Uh oh, we can\'t create this account. Try again in a few minutes and check your internet connection. '; - } -} - -export function getLogoutErrorMessage() { - return 'Sorry, error attempting to log out.'; -} - -export function createErrorLogger(api) { - return () => (next) => (action) => { - if (_.get(action, 'error', false) === true) { - let err = _.get(action, 'payload', {}); - if (!err.debug) { - err.debug = err.message || 'Unknown error'; - } - api.errors.log( - err, - _.get(action, 'meta.metric.eventName', null), - _.omit(_.get(action, 'meta.metric.properties', {}), 'error') - ); - } - return next(action); - }; -} - -export function UnsupportedError(currentVersion, requiredVersion) { - this.name = 'UnsupportedError'; - this.message = `Uploader version ${currentVersion} is no longer supported; version ${requiredVersion} or higher is required.`; -} - -UnsupportedError.prototype = _.create(Error.prototype); -UnsupportedError.prototype.constructor = UnsupportedError; diff --git a/app/utils/metrics.js b/app/utils/metrics.js deleted file mode 100644 index 123ca7ebc..000000000 --- a/app/utils/metrics.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * == BSD2 LICENSE == - * Copyright (c) 2016, Tidepool Project - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the associated License, which is identical to the BSD 2-Clause - * License as published by the Open Source Initiative at opensource.org. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the License for more details. - * - * You should have received a copy of the License along with this program; if - * not, you can obtain one from Tidepool Project at tidepool.org. - * == BSD2 LICENSE == - */ - -import _ from 'lodash'; - -const NONE_PROVIDED = 'No Event Name Provided'; - -export function createMetricsTracker(api) { - return () => (next) => (action) => { - if (_.get(action, 'meta.metric', null) !== null) { - api.metrics.track( - _.get(action, 'meta.metric.eventName', NONE_PROVIDED), - _.get(action, 'meta.metric.properties', {}) - ); - } - if (_.get(action, 'payload.state.meta.metric', null) !== null) { - api.metrics.track( - _.get(action, 'payload.state.meta.metric.eventName', NONE_PROVIDED), - _.get(action, 'payload.state.meta.metric.properties', {}) - ); - } - return next(action); - }; -} diff --git a/app/web.html b/app/web.html index ce0298326..5ccd3560a 100644 --- a/app/web.html +++ b/app/web.html @@ -6,18 +6,29 @@ + -
    +
    +
    + + +
    + +
    + + +
    + + +
    +