async function selectDriveDialog (opts = {}) {
  // validate
  assert(opts && typeof opts === 'object', 'Must pass an options object')
  assert(!opts.title || typeof opts.title === 'string', '.title must be a string')
  assert(!opts.buttonLabel || typeof opts.buttonLabel === 'string', '.buttonLabel must be a string')
  assert(!opts.tag || typeof opts.tag === 'string', '.tag must be a string')
  assert(!opts.writable || typeof opts.writable === 'boolean', '.writable must be a boolean')
  assert(!opts.allowMultiple || typeof opts.allowMultiple === 'boolean', '.allowMultiple must be a boolean')
  assert(!opts.template || typeof opts.template === 'string', '.template must be a string')
  if (opts.template && !isHyperUrl(opts.template)) {
    throw new Error('.template must be a hyper:// URL')
  }

  // initiate the modal
  var res
  try {
    res = await modals.create(this.sender, 'select-drive', opts)
    if (res && res.gotoCreate) {
      if (opts.template) {
        res = await modals.create(this.sender, 'fork-drive', {
          url: opts.template,
          forks: [{url: opts.template}],
          detached: true,
          isTemplate: true,
          title: '',
          description: '',
          tags: [opts.tag]
        })
      } else {
        res = await modals.create(this.sender, 'create-drive', {tags: [opts.tag]})
        if (res && res.gotoSync) {
          await modals.create(this.sender, 'folder-sync', {url: res.url, closeAfterSync: true})
        }
      }
    }
  } catch (e) {
    if (e.name !== 'Error') {
      throw e // only rethrow if a specific error
    }
  }
  if (!res) throw new UserDeniedError()
  return res.urls || res.url
}
@param {Object} [opts]
@param {string} [opts.title]
@param {string} [opts.buttonLabel]
@param {boolean} [opts.writable]
@param {string} [opts.tag]
@param {boolean} [opts.allowMultiple]
@param {string} [opts.template]
@returns {Promise<string|string[]>}