iT邦幫忙

2022 iThome 鐵人賽

DAY 29
0
AI & Data

預測惱人的人事物:跟我一起學習如何用資料分析來避開他們系列 第 29

實作:依照星期幾之不同,在特定時間通知

  • 分享至 

  • xImage
  •  

進度

看到有系列文是超過 30 篇,筆者可以暫時鬆口氣,不用擔心 30 天處理不完~

然而仍希望能順利在預訂的期限內完成目標,完成以下所有 Required 的項目!

今天主要著重在 1.1 實際依照星期幾來通知

1. Required

  1. 實際依照星期幾來通知
  2. 輸出記錄的資料
  3. 打包成 apk 在實體機載入

2. Optional

  1. UI 的按鈕優化
  2. 新增 App 圖示

把預測時間建成常數

讓我們把先前預測的結果,以常數的方式建立吧:
AnnoyancePrediction/src/constants.js

export const NIGHT_PREDICTION = [
  {
    weekday: 'fri',
    time: '21:19',
  },
  {
    weekday: 'sat',
    time: '21:19',
  },
  {
    weekday: 'sun',
    time: '21:19',
  },
  {
    weekday: 'mon',
    time: '21:15',
  },
  {
    weekday: 'thu',
    time: '21:15',
  },
  {
    weekday: 'tue',
    time: '21:14',
  },
  {
    weekday: 'wed',
    time: '21:10',
  },
];


export const DAY_PREDICTION = '11:45';

另外讓我們在同一個檔案加入星期幾轉換為 ISO Weekday 的表,方便之後轉換:

export const ISO_WEEKDAY_DICT = {
  mon: 1,
  tue: 2,
  wed: 3,
  thu: 4,
  fri: 5,
  sat: 6,
  sun: 7,
};

計算時間

讓我們進到 notification.js

import moment from 'moment';
import {DAY_PREDICTION, ISO_WEEKDAY_DICT, NIGHT_PREDICTION} from './constants';

moment.js 是一款可以讓操作時間更方便的套件,讓我們安裝並 import 進我們的專案。

筆者另外建立以下三個轉換天數小時分鐘為毫秒的 function:

export const daysToMilliseconds = days => {
  return days * 24 * 60 * 60 * 1000;
};

export const hoursToMilliseconds = hours => {
  return hours * 60 * 60 * 1000;
};

export const minsToMilliseconds = mins => {
  return mins * 60 * 1000;
};

並且,為了每七天重新建立通知的 event,我們獨立建立一個七天以毫秒為單位的常數輸出:

export const sevenDaysInterval = daysToMilliseconds(7);

修改 onCreateTriggerNotification

為了讓特定的時間傳入,以及支援客製化訊息,讓我們加入兩個參數 time & message

export async function onCreateTriggerNotification(
  time: String,
  message: String,
) {
  const trigger: TimestampTrigger = {
    type: TriggerType.TIMESTAMP,
    timestamp: time,
  };

  const channelId = await notifee.createChannel({
    id: 'default',
    name: 'Default Channel',
  });

  await notifee.createTriggerNotification(
    {
      title: '關門通知',
      body: message ? message : '樓下關門即將發生',
      android: {
        channelId,
        pressAction: {
          id: 'default',
        },
      },
    },
    trigger,
  );
}

並且,讓我們額外建立一個 function onCreateTriggerNotificationByWeekday 來封裝 onCreateTriggerNotification 這個底層的功能:

export const onCreateTriggerNotificationByWeekday = async (
  time: String,
  weekday: String,
) => {
  const paramISOWeekday: Number = ISO_WEEKDAY_DICT[weekday];

  const weekdayNow = moment().isoWeekday();

  const weekdayDiff = weekdayNow - paramISOWeekday;

  const daysDiff =
    weekdayDiff === 0
      ? 0
      : weekdayDiff < 0
      ? Math.abs(weekdayDiff)
      : 7 - weekdayDiff;

  const [hours, mins] = time.split(':');

  const absoluteTime = moment()
    .add(daysDiff, 'days')
    // 原先以為要 +8
    // UTC +8
    // .subtract(1, 'days')
    // .add(hoursLeft, 'hours')
    // .add(hours, 'hours')
    // .add(8, 'hours');
    .set('hour', hours)
    .set('minute', mins)
    // 10 mins before
    .subtract(10, 'minutes');

  // 如果今天的時間已經超過,那麼就設定七天以後
  const absoluteTimeInMs =
    absoluteTime.valueOf() > Date.now()
      ? absoluteTime.valueOf()
      : absoluteTime.valueOf() + sevenDaysInterval;

  const message = `樓下關門即將在 ${time} 發生`;

  onCreateTriggerNotification(absoluteTimeInMs, message);
};

讓我們在這層處理好時間轉換成毫秒、客製化訊息以後,再傳入 onCreateTriggerNotification 呼叫。

時區的部分,原先以為要 +8 處理,但後來筆者有想到可以在 useEffect 加入這一行:

 process.env.TZ = 'Asia/Taipei';

根據筆者的理解,如此操作就會統一使用我們臺灣的時區了。

最後,讓我們建立一個批次處理每天各自通知的 function setNotificationByWeekDay

export const setNotificationByWeekDay = () => {
  const nightPrediction = NIGHT_PREDICTION;
  const dayPrediction = DAY_PREDICTION;

  for (const predict of nightPrediction) {
    const {weekday, time} = predict;
    // night notification
    onCreateTriggerNotificationByWeekday(time, weekday);

    // morning notification
    onCreateTriggerNotificationByWeekday(dayPrediction, weekday);
  }
};

包好之後放在 useEffect

  useEffect(() => {
    process.env.TZ = 'Asia/Taipei';

    checkAndroidBackgroundRestrictions();
    setNotificationByWeekDay();

    // trigger every 7 days
    setInterval(setNotificationByWeekDay, sevenDaysInterval);
  }, []);

稍微改一下通知時間,讓我們看一下成果:
https://ithelp.ithome.com.tw/upload/images/20221015/201413576tJY0AfoOb.png

確實成功在十分鐘前提醒!

今天收工!

參考資料


上一篇
實作 Notifee 背景通知 & 取消系統背景執行限制 & 拆分模組
下一篇
輸出儲存的記錄 & 安裝 app 於實體手機
系列文
預測惱人的人事物:跟我一起學習如何用資料分析來避開他們38
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言