UNPKG

6.81 kBJavaScriptView Raw
1const CLI = require('clui')
2const Spinner = CLI.Spinner
3const inquirer = require('inquirer')
4const chalk = require('chalk')
5const db = require('./db')
6const { parseAndFormatMail, quickMailParse } = require('./util')
7const { REPLY, HOME, CREATE } = require('./constants')
8const format = require('./format')
9const Client = require('./client')
10const { Subject } = require('rxjs')
11
12inquirer.registerPrompt('lazy-list', require('inquirer-plugin-lazy-list'))
13
14class Gmail {
15 constructor (accounts) {
16 this.accounts = accounts
17 this.status = new Spinner('Loading...')
18 this.ui = new inquirer.ui.BottomBar()
19 this.state = {
20 client: undefined,
21 account: {},
22 page: 1,
23 next: undefined,
24 filter: undefined,
25 token: undefined
26 }
27 this.prompts = new Subject()
28 this.inquirer = inquirer.prompt(this.prompts)
29 this.subscribe()
30 }
31
32 setState (state) {
33 this.state = {
34 ...this.state,
35 ...state
36 }
37 }
38
39 renderSettings () {
40 console.log(
41 chalk.yellow(
42 JSON.stringify(this.accounts, null, 2)
43 )
44 )
45 this.renderMain()
46 }
47
48 async renderReply (mail, threadId) {
49 this.prompts.next({
50 type: 'input',
51 name: 'subject',
52 message: 'Subject',
53 default: mail && mail.subject
54 ? mail.subject
55 : ''
56 }, {
57 type: 'input',
58 name: 'recipient',
59 message: 'To',
60 default: mail && mail.from && mail.from.value[0].address
61 ? mail.from.value[0].address
62 : ''
63 }, {
64 type: 'input',
65 name: 'draft',
66 message: 'Compose message'
67 }, {
68 type: 'confirm',
69 name: 'confirmation',
70 message: 'Send?'
71 })
72 this.createConfirmationHandler(threadId)
73 }
74
75 renderAccounts (accounts) {
76 this.prompts.next({
77 name: 'handleAccounts',
78 type: 'list',
79 message: 'Accounts',
80 choices: Object.keys(accounts).map(key =>
81 ({ name: key, value: key })
82 )
83 })
84 }
85
86 async configure (type = 'lazy-list') {
87 const now = Date.now()
88 if (
89 !this.client.account.tokens.access_token ||
90 this.client.account.tokens.expiry_date < now
91 ) {
92 require('./server')
93 }
94 await this.client.fetchMessages()
95 const messages = db.get('messages').value()
96 const emails = format(messages).map(message => ({
97 value: message.id,
98 name: `${message.headers.subject} (${message.headers.from})`
99 }))
100 this.renderInbox(emails, type)
101 }
102
103 async renderMain () {
104 this.prompts.next(HOME)
105 }
106
107 async renderMessage (id) {
108 let {
109 source,
110 message,
111 raw
112 } = await this.client.getMessage(id)
113 const lines = await parseAndFormatMail(source)
114 const mail = await quickMailParse(source)
115 this.ui.log.write(lines.join('\n'))
116 this.prompts.next(REPLY)
117
118 Gmail.prototype.handleMessage = function ({ answer }) {
119 let { threadId } = message
120 let messageId = id
121 const handlers = {
122 back: () => {
123 this.configure()
124 },
125 reply: () => {
126 this.renderReply(mail, threadId)
127 },
128 delete: () => {
129 this.client.deleteMessage(messageId)
130 this.configure()
131 },
132 home: () => {
133 this.renderMain()
134 },
135 exit: () => {
136 process.exit = 0
137 }
138 }
139 return handlers[answer]()
140 }
141 }
142
143 async inboxViewPrompts (answers, messages) {
144 const handler = {
145 compose: () => {
146 inquirer.prompt(...CREATE).then((answers) => {
147 const { text, subject, recipient } = answers
148 const sender = this.state.account.emailAddress
149 this.client.send({ subject, recipient, sender, text })
150 }).then(() => this.configure())
151 },
152 checkbox: () => {
153 this.configure('checkbox')
154 },
155 exit: () => {
156 process.exit = 0
157 },
158 search: () => {
159 this.prompts.next({
160 type: 'input',
161 name: 'search',
162 message: 'Search'
163 })
164 }
165 }
166 if (handler[answers.menu]) {
167 return handler[answers.menu]()
168 }
169 return this.renderMessage(answers, messages)
170 }
171
172 async renderInbox (choices, type = 'lazy-list') {
173 // let options = [
174 // new inquirer.Separator(),
175 // { name: 'Home', value: 'home' },
176 // { name: 'Exit', value: 'exit' },
177 // { name: 'Search', value: 'search' },
178 // { name: 'Bulk', value: 'bulk' },
179 // { name: `Compose ${emoji.message}`, value: 'compose' }
180 // ]
181
182 // choices.unshift(new inquirer.Separator())
183 // choices = choices.concat(options)
184 const fetchMore = async () => {
185 this.state.page++
186 await this.client.fetchMessages(this.state.page)
187 const messages = db.get('messages').value()
188 const formattedMessages = format(messages)
189 const emails = formattedMessages.map(message => ({
190 value: message.id,
191 name: `${message.headers.subject} (${message.headers.from})`
192 }))
193 return emails
194 }
195
196 let pageSize = 10
197 this.prompts.next({
198 name: 'handleInbox',
199 type,
200 message: 'Inbox',
201 pageSize: pageSize,
202 choices,
203 onChange: (state, eventType) => {
204 let index = state.selected
205 let listLength = state.opt.choices.realLength
206 let shouldFetchMore = (index + pageSize) > listLength
207 if (
208 eventType === 'onDownKey' &&
209 shouldFetchMore
210 ) {
211 return fetchMore(state.opt.choices.length)
212 }
213 }
214 })
215 }
216
217 createConfirmationHandler (threadId) {
218 Gmail.prototype.handleConfirmation = function (answers) {
219 if (answers.confirmation) {
220 const text = answers.draft
221 const { recipient, subject } = answers
222 this.client.reply({
223 text,
224 subject,
225 recipient,
226 threadId,
227 sender: this.state.account.emailAddress
228 })
229 console.log(chalk.green('Success!'))
230 this.configure()
231 }
232 }
233 }
234
235 handleMain ({ answer }) {
236 const handlers = {
237 authorize: () => {
238 require('./server')
239 },
240 exit: () => {
241 process.exit = 0
242 },
243 settings: () => {
244 this.renderSettings()
245 },
246 inbox: () => {
247 this.renderAccounts(this.accounts)
248 }
249 }
250 console.log('handleMain', answer)
251 return handlers[answer]()
252 }
253
254 async handleAccounts ({ answer }) {
255 let account = this.accounts[answer]
256 this.client = await Client.create(account)
257 this.configure()
258 }
259
260 handleInbox ({ answer }) {
261 this.renderMessage(answer)
262 }
263
264 subscribe () {
265 this.inquirer.ui.process.subscribe(
266 (answer) => {
267 if (this[answer.name]) {
268 return this[answer.name](answer)
269 }
270 },
271 () => {},
272 () => {}
273 )
274 }
275}
276
277module.exports = Gmail