Passed
Push — master ( 1a6db4...d22713 )
by Muhammad Dyas
01:40
created

PollCard.buildInfoSection   A

Complexity

Conditions 2

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 2
1
import BaseCard from './BaseCard';
2
import {ClosableType, LocaleTimezone, PollState, Voter} from '../helpers/interfaces';
3
import {chat_v1 as chatV1} from '@googleapis/chat';
4
import {ICON_URL_48X48} from '../config/default';
5
import {progressBarText} from '../helpers/vote';
6
import {createButton} from '../helpers/cards';
7
8
export default class PollCard extends BaseCard {
9
  protected readonly state: PollState;
10
  protected readonly timezone: LocaleTimezone;
11
12
  constructor(state: PollState, timezone: LocaleTimezone) {
13
    super();
14
    this.state = state;
15
    this.timezone = timezone;
16
  }
17
18
  create() {
19
    this.buildHeader();
20
    this.buildSections();
21
    this.buildButtons();
22
    this.buildFooter();
23
    this.card.name = this.getSerializedState();
24
    return this.card;
25
  }
26
27
  buildHeader() {
28
    if (this.state.topic.length > 40) {
29
      const widgetHeader = this.sectionHeader();
30
      this.card.sections!.push(widgetHeader);
31
    } else {
32
      this.card.header = this.cardHeader();
33
    }
34
    this.buildInfoSection();
35
  }
36
37
  buildInfoSection() {
38
    if (this.state.voteLimit === 0 || (this.state.voteLimit && this.state.voteLimit > 1)) {
39
      const widgetHeader = this.sectionInfo();
40
      this.card.sections!.push(widgetHeader);
41
    }
42
  }
43
44
  getAuthorName() {
45
    return this.state.author?.displayName ?? '';
46
  }
47
48
  getSerializedState() {
49
    return JSON.stringify(this.state);
50
  }
51
52
  cardHeader(): chatV1.Schema$GoogleAppsCardV1CardHeader {
53
    return {
54
      title: this.state.topic,
55
      subtitle: `Posted by ${this.getAuthorName()}`,
56
      imageUrl: ICON_URL_48X48,
57
      imageType: 'CIRCLE',
58
    };
59
  }
60
61
  sectionHeader(): chatV1.Schema$GoogleAppsCardV1Section {
62
    return {
63
      widgets: [
64
        {
65
          'decoratedText': {
66
            'text': this.state.topic,
67
            'wrapText': true,
68
            'bottomLabel': `Posted by ${this.getAuthorName()}`,
69
            'startIcon': {
70
              'altText': 'Absolute Poll',
71
              'iconUrl': ICON_URL_48X48,
72
              'imageType': 'SQUARE',
73
            },
74
          },
75
        },
76
      ],
77
    };
78
  }
79
  sectionInfo(): chatV1.Schema$GoogleAppsCardV1Section {
80
    return {
81
      widgets: [
82
        {
83
          'decoratedText': {
84
            'text': '',
85
            'wrapText': true,
86
            'topLabel': `This poll allow multiple votes. Max Votes: ${this.state.voteLimit || 'No limit'}`,
87
          },
88
        },
89
      ],
90
    };
91
  }
92
93
  buildSections() {
94
    const votes: Array<Array<Voter>> = Object.values(this.state.votes ?? {});
95
    const totalVotes: number = votes.reduce((sum, vote) => sum + vote.length, 0);
96
    for (let i = 0; i < this.state.choices.length; ++i) {
97
      const creator = this.state.choiceCreator?.[i] ?? '';
98
      const section = this.choiceSection(i, totalVotes, creator);
99
      this.card.sections!.push(section);
100
    }
101
  }
102
103
  buildButtons() {
104
    const buttons = [];
105
    if (this.state.optionable) {
106
      buttons.push(createButton('Add Option', 'add_option_form', 'OPEN_DIALOG'));
107
    }
108
    const isClosable = this.state.type === undefined || this.state.type !== ClosableType.UNCLOSEABLE;
109
110
    if (isClosable) {
111
      const closeButton = createButton('Close Poll', 'close_poll_form', 'OPEN_DIALOG');
112
      if (this.isClosed()) {
113
        closeButton.disabled = true;
114
      }
115
      buttons.push(closeButton);
116
    }
117
118
    if (buttons.length > 0) {
119
      this.card.sections!.push(
120
        {
121
          'widgets': [
122
            {
123
              'buttonList': {
124
                buttons,
125
              },
126
            },
127
          ],
128
        });
129
    }
130
  }
131
132
  buildFooter() {
133
    if (!this.isClosed() && this.state.closedTime) {
134
      const locale = this.timezone.locale;
135
      try {
136
        const closedDate = new Date(this.state.closedTime).toLocaleString(locale, {timeZone: this.timezone.id});
137
        this.card.sections!.push(
138
          {
139
            'widgets': [
140
              {
141
                'decoratedText': {
142
                  'text': `<i>Auto Close at <time> ${closedDate}</time>  ${this.timezone.id} </i>`,
143
                  'startIcon': {
144
                    'knownIcon': 'CLOCK',
145
                    'altText': '@',
146
                  },
147
                },
148
              },
149
            ],
150
          });
151
      } catch (e) {
152
        // the most possible error is because of timezone issue, so just ignore
153
        console.log(e, JSON.stringify(this.timezone));
154
      }
155
    } else if (this.isClosed()) {
156
      this.card.sections!.push(
157
        {
158
          'widgets': [
159
            {
160
              'decoratedText': {
161
                'bottomLabel': `<i>This poll was closed by ${this.state.closedBy}</i>`,
162
              },
163
            },
164
          ],
165
        });
166
    }
167
  }
168
169
  choiceSection(i: number, totalVotes: number, creator = '') {
170
    if (this.state.votes === undefined) {
171
      this.state.votes = {};
172
    }
173
174
    if (this.state.votes[i] === undefined) {
175
      this.state.votes[i] = [];
176
    }
177
    const voteCount = this.state.votes[i].length;
178
    const choiceTag = this.choice(i, this.state.choices[i], voteCount, totalVotes);
179
    if (creator) {
180
      choiceTag.decoratedText!.topLabel = 'Added by ' + creator;
181
    }
182
    const section: chatV1.Schema$GoogleAppsCardV1Section = {
183
      widgets: [choiceTag],
184
    };
185
    if (this.state.votes[i].length > 0 && !this.state.anon) {
186
      section.collapsible = true;
187
      section.uncollapsibleWidgetsCount = 1;
188
      // @ts-ignore: already defined above
189
      section.widgets.push({
190
        'textParagraph': {
191
          'text': this.state.votes[i].map((u) => u.name).join(', '),
192
        },
193
      });
194
    }
195
    return section;
196
  }
197
198
  choice(index: number, text: string, voteCount: number, totalVotes: number): chatV1.Schema$GoogleAppsCardV1Widget {
199
    const progressBar = progressBarText(voteCount, totalVotes);
200
    const voteButton: chatV1.Schema$GoogleAppsCardV1Button = {
201
      text: 'vote',
202
      onClick: {
203
        action: {
204
          function: 'vote',
205
          parameters: [
206
            {
207
              key: 'index',
208
              value: index.toString(10),
209
            },
210
          ],
211
        },
212
      },
213
    };
214
    if (this.state.voteLimit !== undefined && this.state.voteLimit !== 1) {
215
      voteButton.onClick!.action!.interaction = 'OPEN_DIALOG';
216
      voteButton.onClick!.action!.function = 'vote_form';
217
    }
218
219
    if (this.isClosed()) {
220
      voteButton.disabled = true;
221
    }
222
    return {
223
      decoratedText: {
224
        bottomLabel: `${progressBar} ${voteCount}`,
225
        text: text,
226
        button: voteButton,
227
      },
228
    };
229
  }
230
231
  private isClosed(): boolean {
232
    return !!this.state.closedTime && this.state.closedTime <= Date.now();
233
  }
234
}
235