Completed
Push — master ( 605e3d...3de0bb )
by Muhammad Dyas
14s queued 13s
created

PollCard.buildFooter   A

Complexity

Conditions 4

Size

Total Lines 31
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

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