From 7089072e3fcf026e711f69457f7ed33a1aeac847 Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Mon, 16 Dec 2019 22:50:04 +0700 Subject: [PATCH 001/213] Delete CNAME --- CNAME | 1 - 1 file changed, 1 deletion(-) delete mode 100644 CNAME diff --git a/CNAME b/CNAME deleted file mode 100644 index 269bed0..0000000 --- a/CNAME +++ /dev/null @@ -1 +0,0 @@ -tanpattaya.io \ No newline at end of file From 3c11558bd76bf08fe40484e58ab3916a0a51f56d Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 12:09:26 +0700 Subject: [PATCH 002/213] Add files via upload --- app.json | 5 + game.html | 23 +++++ liff.js | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 299 insertions(+) create mode 100644 app.json create mode 100644 game.html create mode 100644 liff.js diff --git a/app.json b/app.json new file mode 100644 index 0000000..2b5edda --- /dev/null +++ b/app.json @@ -0,0 +1,5 @@ +{ + "name": "LINE Liff Starter", + "description": "Sample LINE LIFF application", + "repository": "https://github.com/line/line-liff-starter" +} \ No newline at end of file diff --git a/game.html b/game.html new file mode 100644 index 0000000..e98f34f --- /dev/null +++ b/game.html @@ -0,0 +1,23 @@ + + + + + + + + Bird Demo + + + + + + + + + diff --git a/liff.js b/liff.js new file mode 100644 index 0000000..2adb7ac --- /dev/null +++ b/liff.js @@ -0,0 +1,271 @@ +// User service UUID: Change this to your generated service UUID +const USER_SERVICE_UUID = '91E4E176-D0B9-464D-9FE4-52EE3E9F1552'; // LED, Button +// User service characteristics +const LED_CHARACTERISTIC_UUID = 'E9062E71-9E62-4BC6-B0D3-35CDCD9B027B'; +const BTN_CHARACTERISTIC_UUID = '62FBD229-6EDD-4D1A-B554-5C4E1BB29169'; + +// PSDI Service UUID: Fixed value for Developer Trial +const PSDI_SERVICE_UUID = 'E625601E-9E55-4597-A598-76018A0D293D'; // Device ID +const PSDI_CHARACTERISTIC_UUID = '26E2B12B-85F0-4F3F-9FDD-91D114270E6E'; + +// UI settings +let ledState = false; // true: LED on, false: LED off +let clickCount = 0; + +// -------------- // +// On window load // +// -------------- // + +window.onload = () => { + initializeApp(); +}; + +// ----------------- // +// Handler functions // +// ----------------- // + +function handlerToggleLed() { + ledState = !ledState; + + uiToggleLedButton(ledState); + liffToggleDeviceLedState(ledState); +} + +// ------------ // +// UI functions // +// ------------ // + +function uiToggleLedButton(state) { + const el = document.getElementById("btn-led-toggle"); + el.innerText = state ? "Switch LED OFF" : "Switch LED ON"; + + if (state) { + el.classList.add("led-on"); + } else { + el.classList.remove("led-on"); + } +} + +function uiCountPressButton() { + clickCount++; + + const el = document.getElementById("click-count"); + el.innerText = clickCount; +} + +function uiToggleStateButton(pressed) { + const el = document.getElementById("btn-state"); + + if (pressed) { + el.classList.add("pressed"); + el.innerText = "Pressed"; + } else { + el.classList.remove("pressed"); + el.innerText = "Released"; + } +} + +function uiToggleDeviceConnected(connected) { + const elStatus = document.getElementById("status"); + const elControls = document.getElementById("controls"); + + elStatus.classList.remove("error"); + + if (connected) { + // Hide loading animation + uiToggleLoadingAnimation(false); + // Show status connected + elStatus.classList.remove("inactive"); + elStatus.classList.add("success"); + elStatus.innerText = "Device connected"; + // Show controls + elControls.classList.remove("hidden"); + } else { + // Show loading animation + uiToggleLoadingAnimation(true); + // Show status disconnected + elStatus.classList.remove("success"); + elStatus.classList.add("inactive"); + elStatus.innerText = "Device disconnected"; + // Hide controls + elControls.classList.add("hidden"); + } +} + +function uiToggleLoadingAnimation(isLoading) { + const elLoading = document.getElementById("loading-animation"); + + if (isLoading) { + // Show loading animation + elLoading.classList.remove("hidden"); + } else { + // Hide loading animation + elLoading.classList.add("hidden"); + } +} + +function uiStatusError(message, showLoadingAnimation) { + uiToggleLoadingAnimation(showLoadingAnimation); + + const elStatus = document.getElementById("status"); + const elControls = document.getElementById("controls"); + + // Show status error + elStatus.classList.remove("success"); + elStatus.classList.remove("inactive"); + elStatus.classList.add("error"); + elStatus.innerText = message; + + // Hide controls + elControls.classList.add("hidden"); +} + +function makeErrorMsg(errorObj) { + return "Error\n" + errorObj.code + "\n" + errorObj.message; +} + +// -------------- // +// LIFF functions // +// -------------- // + +function initializeApp() { + liff.init(() => initializeLiff(), error => uiStatusError(makeErrorMsg(error), false)); +} + +function initializeLiff() { + liff.initPlugins(['bluetooth']).then(() => { + liffCheckAvailablityAndDo(() => liffRequestDevice()); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} + +function liffCheckAvailablityAndDo(callbackIfAvailable) { + // Check Bluetooth availability + liff.bluetooth.getAvailability().then(isAvailable => { + if (isAvailable) { + uiToggleDeviceConnected(false); + callbackIfAvailable(); + } else { + uiStatusError("Bluetooth not available", true); + setTimeout(() => liffCheckAvailablityAndDo(callbackIfAvailable), 10000); + } + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + });; +} + +function liffRequestDevice() { + liff.bluetooth.requestDevice().then(device => { + liffConnectToDevice(device); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} + +function liffConnectToDevice(device) { + device.gatt.connect().then(() => { + document.getElementById("device-name").innerText = device.name; + document.getElementById("device-id").innerText = device.id; + + // Show status connected + uiToggleDeviceConnected(true); + + // Get service + device.gatt.getPrimaryService(USER_SERVICE_UUID).then(service => { + liffGetUserService(service); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); + device.gatt.getPrimaryService(PSDI_SERVICE_UUID).then(service => { + liffGetPSDIService(service); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); + + // Device disconnect callback + const disconnectCallback = () => { + // Show status disconnected + uiToggleDeviceConnected(false); + + // Remove disconnect callback + device.removeEventListener('gattserverdisconnected', disconnectCallback); + + // Reset LED state + ledState = false; + // Reset UI elements + uiToggleLedButton(false); + uiToggleStateButton(false); + + // Try to reconnect + initializeLiff(); + }; + + device.addEventListener('gattserverdisconnected', disconnectCallback); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} + +function liffGetUserService(service) { + // Button pressed state + service.getCharacteristic(BTN_CHARACTERISTIC_UUID).then(characteristic => { + liffGetButtonStateCharacteristic(characteristic); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); + + // Toggle LED + service.getCharacteristic(LED_CHARACTERISTIC_UUID).then(characteristic => { + window.ledCharacteristic = characteristic; + + // Switch off by default + liffToggleDeviceLedState(false); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} + +function liffGetPSDIService(service) { + // Get PSDI value + service.getCharacteristic(PSDI_CHARACTERISTIC_UUID).then(characteristic => { + return characteristic.readValue(); + }).then(value => { + // Byte array to hex string + const psdi = new Uint8Array(value.buffer) + .reduce((output, byte) => output + ("0" + byte.toString(16)).slice(-2), ""); + document.getElementById("device-psdi").innerText = psdi; + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} + +function liffGetButtonStateCharacteristic(characteristic) { + // Add notification hook for button state + // (Get notified when button state changes) + characteristic.startNotifications().then(() => { + characteristic.addEventListener('characteristicvaluechanged', e => { + const val = (new Uint8Array(e.target.value.buffer))[0]; + if (val > 0) { + // press + uiToggleStateButton(true); + } else { + // release + uiToggleStateButton(false); + uiCountPressButton(); + } + }); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} + +function liffToggleDeviceLedState(state) { + // on: 0x01 + // off: 0x00 + window.ledCharacteristic.writeValue( + state ? new Uint8Array([0x01]) : new Uint8Array([0x00]) + ).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} From f5c5bac5b119409c924c488ec34ddc4249837d42 Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 12:28:30 +0700 Subject: [PATCH 003/213] Update index.html --- index.html | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 186175a..56b5516 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,12 @@ - + + + + + + + + + ꧁💓 @:꓄ꍏꈤᖘꍏ꓄꓄ꍏꌩ💓꧂BOTS LIFF-APP @@ -22,3 +30,4 @@

+ From 2bb08cba7b64d00635d0516b74e19f2107c3ae4c Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 12:35:11 +0700 Subject: [PATCH 004/213] Update README.md --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index 4c134e5..39e4b83 100644 --- a/README.md +++ b/README.md @@ -56,3 +56,27 @@ The time we greeted you. uses: actions/hello-world-javascript-action@v1 with: who-to-greet: 'Mona the Octocat' +# LINE Things Starter - LIFF App + +This is the sample code of the LIFF App for LINE Things Starter. +The code contains a LIFF application that will work with the Starter firmwares. + +## Setup + +Ensure you have already created a Developer Trial account and generated a service UUID. +To configure the app, change the `USER_SERVICE_UUID` in `liff.js` to your generated service UUID. + +## Publish LIFF App using GitHub Pages + +The LIFF application included in this repository can also be hosted by using GitHub Pages. To host your LIFF app, perform the following: + +1. Fork this repository on GitHub +2. From GitHub Pages settings, select the "master branch" as the "Source" +3. Set the URL of the published GitHub Pages page following with `liff-app/` as the LIFF endpoint URL. +(For Example: https://line.github.io/line-things-starter/liff-app/) + +## Reference + +For details on Bluetooth LE plugin API for LINE Things on LIFF, please refer to the following document (English to follow shortly): + +https://developers.line.biz/ja/reference/line-things/ From d6a795925162306ccde295b4d890673ddafab975 Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 12:36:36 +0700 Subject: [PATCH 005/213] Update index.html --- index.html | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/index.html b/index.html index 56b5516..186175a 100644 --- a/index.html +++ b/index.html @@ -1,12 +1,4 @@ - - - - - - - - - + ꧁💓 @:꓄ꍏꈤᖘꍏ꓄꓄ꍏꌩ💓꧂BOTS LIFF-APP @@ -30,4 +22,3 @@

- From 632ae6e8da4ec56093b5ae0a3440d06867996d42 Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 12:38:07 +0700 Subject: [PATCH 006/213] Update index.html --- index.html | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 186175a..2842bc7 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,12 @@ - + + + + + + + + + ꧁💓 @:꓄ꍏꈤᖘꍏ꓄꓄ꍏꌩ💓꧂BOTS LIFF-APP From 0925b9760953a658a193ce67441b296b98c3ce96 Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:11:32 +0700 Subject: [PATCH 007/213] Add files via upload --- README.md | 58 ------------------------------------------------------- 1 file changed, 58 deletions(-) diff --git a/README.md b/README.md index 39e4b83..b484aa8 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,3 @@ -## Welcome to GitHub Pages - -You can use the [editor on GitHub](https://github.com/tanpattaya/github.io/edit/master/index.md) to maintain and preview the content for your website in Markdown files. - -Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files. - -### Markdown - -Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for - -```markdown -Syntax highlighted code block - -# Header 1 -## Header 2 -### Header 3 - -- Bulleted -- List - -1. Numbered -2. List - -**Bold** and _Italic_ and `Code` text - -[Link](url) and ![Image](src) -``` - -For more details see [GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/). - -### Jekyll Themes - -Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/tanpattaya/aditmadzs.github.io/settings). The name of this theme is saved in the Jekyll `_config.yml` configuration file. - -### Support or Contact - -Having trouble with Pages? Check out our [documentation](https://help.github.com/categories/github-pages-basics/) or [contact support](https://github.com/contact) and we’ll help you sort it out. -# Hello world javascript action - -This action prints "Hello World" or "Hello" + the name of a person to greet to the log. - -## Inputs - -### `who-to-greet` - -**Required** The name of the person to greet. Default `"World"`. - -## Outputs - -### `time` - -The time we greeted you. - -## Example usage - -uses: actions/hello-world-javascript-action@v1 -with: - who-to-greet: 'Mona the Octocat' # LINE Things Starter - LIFF App This is the sample code of the LIFF App for LINE Things Starter. From 93cf7229ad387a2ec332bc7a7ca6251f5fdb4385 Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:21:59 +0700 Subject: [PATCH 008/213] Add files via upload --- liff-starter.js | 282 +++++++++++++++--------------------------------- 1 file changed, 88 insertions(+), 194 deletions(-) diff --git a/liff-starter.js b/liff-starter.js index 40718be..09454ee 100644 --- a/liff-starter.js +++ b/liff-starter.js @@ -1,194 +1,88 @@ -window.onload = function (e) { - liff.init(function () { - getP(); - }); -}; - -function getP(){ - var tipe = getParameterByName('type') - if (!tipe) { - document.getElementById('textx').addEventListener('click', function () { - liff.sendMessages([{ - type: 'text', - text: atob('bGluZTovL2FwcC8xNjAyNjg3MzA4LUdYcTRWdms5P3R5cGU9dGV4dCZ0ZXh0PVlvdXIlMjBUZXh0Cgp0eXBlID0+IHRleHQKdGV4dCA9PiB5b3VyIHRleHQ=') - }]).then(function () { - liff.closeWindow(); - }); - }); - document.getElementById('imagex').addEventListener('click', function () { - liff.sendMessages([{ - type: 'text', - text: atob('bGluZTovL2FwcC8xNjAyNjg3MzA4LUdYcTRWdms5P3R5cGU9aW1hZ2UmaW1nPWh0dHBzOi8vd2FsbHBhcGVyc3R1ZGlvMTAuY29tL3N0YXRpYy93cGRiL3dhbGxwYXBlcnMvMTAwMHg1NjMvMTY4ODkxLmpwZwoKdHlwZSA9PiBpbWFnZQppbWcgPT4gTGluayAobXVzdCBiZSBIVFRQUyk=') - }]).then(function () { - liff.closeWindow(); - }); - }); - document.getElementById('videox').addEventListener('click', function () { - liff.sendMessages([{ - type: 'text', - text: atob('bGluZTovL2FwcC8xNjAyNjg3MzA4LUdYcTRWdms5P3R5cGU9dmlkZW8mb2N1PWh0dHBzOi8vdGlueXVybC5jb20veThvZzNvcjUmcGl1PWh0dHBzOi8vaW1hZ2VzNi5hbHBoYWNvZGVycy5jb20vNzEwL3RodW1iLTM1MC03MTAxMzIucG5nCgp0eXBlID0+IHZpZGVvCm9jdSA9PiB2aWRlbyB1cmwKcGl1ID0+IHByZXZpZXcgaW1hZ2U=') - }]).then(function () { - liff.closeWindow(); - }); - }); - document.getElementById('audiox').addEventListener('click', function () { - liff.sendMessages([{ - type: 'text', - text: atob('bGluZTovL2FwcC8xNjAyNjg3MzA4LUdYcTRWdms5P3R5cGU9YXVkaW8mbGluaz1odHRwczovL3BsYXRlbGV0cy5mdW4vcHVibGljL3NvdW5kcy9tdXNpYy5tcDM=') - }]).then(function () { - liff.closeWindow(); - }); - }); - document.getElementById('stickerx').addEventListener('click', function () { - liff.sendMessages([{ - type: 'text', - text: atob('V2l0aCBBbmltYXRpb246CmxpbmU6Ly9hcHAvMTYwMjY4NzMwOC1HWHE0VnZrOT90eXBlPXN0aWNrZXImc3RrPWFuaW0mc2lkPTMyMTI4MjMxJnBrZz0zMDk5MzEyCgpObyBBbmltYXRpb246CmxpbmU6Ly9hcHAvMTYwMjY4NzMwOC1HWHE0VnZrOT90eXBlPXN0aWNrZXImc3RrPW5vYW5pbSZzaWQ9MzIxMjgyMzEmcGtnPTMwOTkzMTIKCnR5cGUgPT4gc3RpY2tlcgpzdGsgPT4gYW5pbSAvIG5vYW5pbQpzaWQgPT4gc3RpY2tlciBpZApwa2cgPT4gcGFja2FnZXMgaWQ=') - }]).then(function () { - liff.closeWindow(); - }); - }); - } else { - makeText(); - makeImage(); - makeVideo(); - makeAudio(); - makeSticker(); - meProfile(); - } - } - -function getParameterByName(name, url) { - if (!url) url = window.location.href; - name = name.replace(/[\[\]]/g, '\\$&'); - var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'), - results = regex.exec(url); - if (!results) return null; - if (!results[2]) return ''; - return decodeURIComponent(results[2].replace(/\+/g, ' ')); -} -function getProfile(){ - liff.getProfile().then(function (profile) { - document.getElementById('userid').textContent = 'Hai ' + profile.displayName; - document.getElementById('main').src = profile.pictureUrl; - document.getElementById('close').addEventListener('click', function () { - liff.closeWindow(); - }); - }); -} - -function makeText(){ - var tipe = getParameterByName('type'); - if (tipe === 'text') { - liff.sendMessages([{ - type: 'text', - text: getParameterByName('text') - }]).then(function () { - liff.closeWindow(); - }); - } -} - -function makeImage(){ - var tipe = getParameterByName('type'); - if (tipe === 'image') { - liff.sendMessages([{ - type: 'image', - originalContentUrl: getParameterByName('img'), - previewImageUrl: getParameterByName('img') - }]).then(function () { - liff.closeWindow(); - }); - } -} - -function makeVideo(){ - var tipe = getParameterByName('type'); - if (tipe === 'video') { - liff.sendMessages([{ - type: 'video', - originalContentUrl: getParameterByName('ocu'), - previewImageUrl: getParameterByName('piu') - }]).then(function () { - liff.closeWindow(); - }); - } -} - -function makeAudio(){ - var tipe = getParameterByName('type'); - if (tipe === 'audio') { - liff.sendMessages([{ - type: 'audio', - originalContentUrl: getParameterByName('link'), - duration: 60000 - }]).then(function () { - liff.closeWindow(); - }); - } -} - -function makeSticker(){ - var tipe = getParameterByName('type'); - if (tipe === 'sticker') { - var stk = getParameterByName('stk'); - var sid = getParameterByName('sid'); - var pkg = getParameterByName('pkg'); - var ep = ''; - if (stk === 'anim') { - ep = "/IOS/sticker_animation@2x.png"; - } else { - ep = "/IOS/sticker@2x.png"; - } - liff.sendMessages([{ - type: "template", - altText: "Sticker", - template: { - type: "image_carousel", - columns: [{ - imageUrl: "https://stickershop.line-scdn.net/stickershop/v1/sticker/"+sid+ep, - action: { - type: "uri", - uri: "line://shop/sticker/detail/"+pkg}} - ] - } - }]).then(function () { - liff.closeWindow(); - }); - } -} - -function meProfile(){ - var tipe = getParameterByName('type'); - liff.getProfile().then(function (prof) { - var stat = prof.statusMessage; - if (stat == null) { - var stat = " - "; - } - if (stat.length > 60) { - var stat = "Status Message is to long! Max 60 words"; - } - if (tipe === 'profile') { - liff.sendMessages([{ - type: "template", - altText: "Profile "+prof.displayName, - template: { - type: "buttons", - thumbnailImageUrl: prof.pictureUrl, - imageAspectRatio: "square", - imageSize: "cover", - title: prof.displayName, - text: stat, - actions: [ - { - type:"uri", - label:"Me", - uri:"line://app/1653536815-WyNbrkGr?type=profile" - } - ] - } - }]).then(function () { - liff.closeWindow(); - }); - } - }); -} +window.onload = function (e) { + liff.init(function (data) { + initializeApp(data); + }); +}; + +function initializeApp(data) { + document.getElementById('languagefield').textContent = data.language; + document.getElementById('viewtypefield').textContent = data.context.viewType; + document.getElementById('useridfield').textContent = data.context.userId; + document.getElementById('utouidfield').textContent = data.context.utouId; + document.getElementById('roomidfield').textContent = data.context.roomId; + document.getElementById('groupidfield').textContent = data.context.groupId; + + // openWindow call + document.getElementById('openwindowbutton').addEventListener('click', function () { + liff.openWindow({ + url: 'https://line.me' + }); + }); + + // closeWindow call + document.getElementById('closewindowbutton').addEventListener('click', function () { + liff.closeWindow(); + }); + + // sendMessages call + document.getElementById('sendmessagebutton').addEventListener('click', function () { + liff.sendMessages([{ + type: 'text', + text: "You've successfully sent a message! Hooray!" + }, { + type: 'sticker', + packageId: '2', + stickerId: '144' + }]).then(function () { + window.alert("Message sent"); + }).catch(function (error) { + window.alert("Error sending message: " + error); + }); + }); + + // get access token + document.getElementById('getaccesstoken').addEventListener('click', function () { + const accessToken = liff.getAccessToken(); + document.getElementById('accesstokenfield').textContent = accessToken; + toggleAccessToken(); + }); + + // get profile call + document.getElementById('getprofilebutton').addEventListener('click', function () { + liff.getProfile().then(function (profile) { + document.getElementById('useridprofilefield').textContent = profile.userId; + document.getElementById('displaynamefield').textContent = profile.displayName; + + const profilePictureDiv = document.getElementById('profilepicturediv'); + if (profilePictureDiv.firstElementChild) { + profilePictureDiv.removeChild(profilePictureDiv.firstElementChild); + } + const img = document.createElement('img'); + img.src = profile.pictureUrl; + img.alt = "Profile Picture"; + profilePictureDiv.appendChild(img); + + document.getElementById('statusmessagefield').textContent = profile.statusMessage; + toggleProfileData(); + }).catch(function (error) { + window.alert("Error getting profile: " + error); + }); + }); +} + +function toggleAccessToken() { + toggleElement('accesstokendata'); +} + +function toggleProfileData() { + toggleElement('profileinfo'); +} + +function toggleElement(elementId) { + const elem = document.getElementById(elementId); + if (elem.offsetWidth > 0 && elem.offsetHeight > 0) { + elem.style.display = "none"; + } else { + elem.style.display = "block"; + } +} \ No newline at end of file From d70e6d9d0591b1cacde6905bd2136e8cad86858d Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:27:59 +0700 Subject: [PATCH 009/213] Update .gitignore --- .gitignore | 82 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 5d947ca..2eeeb4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,64 @@ -# Build and Release Folders -bin-debug/ -bin-release/ -[Oo]bj/ -[Bb]in/ - -# Other files and folders -.settings/ - -# Executables -*.swf -*.air -*.ipa -*.apk - -# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` -# should NOT be excluded as they contain compiler settings and other important -# information for Eclipse / Flash Builder. +# บันทึก +บันทึก +* .log +NPM-debug.log * +เส้นด้าย debug.log * +เส้นด้าย error.log * + +# ข้อมูลรันไทม์ +PIDs +* .pid +* .seed +* .pid.lock + +# ไดเรกทอรีสำหรับ libs ที่สร้างโดย jscoverage / JSCover +lib-COV + +# ไดเรกทอรีที่ครอบคลุมที่ใช้โดยเครื่องมือเช่น istanbul +ความคุ้มครอง + +# ครอบคลุมการทดสอบ NYC +.nyc_output + +# ที่เก็บข้อมูลกลางที่รุนแรง (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# ไดเรกทอรีพึ่งพา Bower (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# addons ไบนารีที่คอมไพล์แล้ว (https://nodejs.org/api/addons.html) +สร้าง / ที่วางจำหน่าย + +# ไดเรกทอรีการพึ่งพา +node_modules / +jspm_packages / + +# ไฟล์ประกาศ TypeScript v1 +typings / + +# ไดเรกทอรีแคช npm ที่เป็นตัวเลือก +.npm + +# แคช eslint ที่เป็นตัวเลือก +.eslintcache + +# ประวัติ REPL เพิ่มเติม +.node_repl_history + +# เอาต์พุตของ 'npm pack' +* .tgz + +# ไฟล์ Yarn Integrity +.yarn สมบูรณ์ + +# ไฟล์ตัวแปรสภาพแวดล้อม dotenv +.env + +# next.js สร้างเอาต์พุต +.ต่อไป + +# ไฟล์ mac +* DS_Store From 6746d64f866a3907fcac86647350bdc60fc1a645 Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:35:23 +0700 Subject: [PATCH 010/213] Create liff.js --- "\340\271\201\340\270\255\340\270\233 liff/liff.js" | 1 + 1 file changed, 1 insertion(+) create mode 100644 "\340\271\201\340\270\255\340\270\233 liff/liff.js" diff --git "a/\340\271\201\340\270\255\340\270\233 liff/liff.js" "b/\340\271\201\340\270\255\340\270\233 liff/liff.js" new file mode 100644 index 0000000..8b13789 --- /dev/null +++ "b/\340\271\201\340\270\255\340\270\233 liff/liff.js" @@ -0,0 +1 @@ + From 13d4b777885dd979bca561823d57382095f03038 Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:36:32 +0700 Subject: [PATCH 011/213] Add files via upload --- .../liff.js" | 270 ++++++++++++++++++ 1 file changed, 270 insertions(+) diff --git "a/\340\271\201\340\270\255\340\270\233 liff/liff.js" "b/\340\271\201\340\270\255\340\270\233 liff/liff.js" index 8b13789..2adb7ac 100644 --- "a/\340\271\201\340\270\255\340\270\233 liff/liff.js" +++ "b/\340\271\201\340\270\255\340\270\233 liff/liff.js" @@ -1 +1,271 @@ +// User service UUID: Change this to your generated service UUID +const USER_SERVICE_UUID = '91E4E176-D0B9-464D-9FE4-52EE3E9F1552'; // LED, Button +// User service characteristics +const LED_CHARACTERISTIC_UUID = 'E9062E71-9E62-4BC6-B0D3-35CDCD9B027B'; +const BTN_CHARACTERISTIC_UUID = '62FBD229-6EDD-4D1A-B554-5C4E1BB29169'; +// PSDI Service UUID: Fixed value for Developer Trial +const PSDI_SERVICE_UUID = 'E625601E-9E55-4597-A598-76018A0D293D'; // Device ID +const PSDI_CHARACTERISTIC_UUID = '26E2B12B-85F0-4F3F-9FDD-91D114270E6E'; + +// UI settings +let ledState = false; // true: LED on, false: LED off +let clickCount = 0; + +// -------------- // +// On window load // +// -------------- // + +window.onload = () => { + initializeApp(); +}; + +// ----------------- // +// Handler functions // +// ----------------- // + +function handlerToggleLed() { + ledState = !ledState; + + uiToggleLedButton(ledState); + liffToggleDeviceLedState(ledState); +} + +// ------------ // +// UI functions // +// ------------ // + +function uiToggleLedButton(state) { + const el = document.getElementById("btn-led-toggle"); + el.innerText = state ? "Switch LED OFF" : "Switch LED ON"; + + if (state) { + el.classList.add("led-on"); + } else { + el.classList.remove("led-on"); + } +} + +function uiCountPressButton() { + clickCount++; + + const el = document.getElementById("click-count"); + el.innerText = clickCount; +} + +function uiToggleStateButton(pressed) { + const el = document.getElementById("btn-state"); + + if (pressed) { + el.classList.add("pressed"); + el.innerText = "Pressed"; + } else { + el.classList.remove("pressed"); + el.innerText = "Released"; + } +} + +function uiToggleDeviceConnected(connected) { + const elStatus = document.getElementById("status"); + const elControls = document.getElementById("controls"); + + elStatus.classList.remove("error"); + + if (connected) { + // Hide loading animation + uiToggleLoadingAnimation(false); + // Show status connected + elStatus.classList.remove("inactive"); + elStatus.classList.add("success"); + elStatus.innerText = "Device connected"; + // Show controls + elControls.classList.remove("hidden"); + } else { + // Show loading animation + uiToggleLoadingAnimation(true); + // Show status disconnected + elStatus.classList.remove("success"); + elStatus.classList.add("inactive"); + elStatus.innerText = "Device disconnected"; + // Hide controls + elControls.classList.add("hidden"); + } +} + +function uiToggleLoadingAnimation(isLoading) { + const elLoading = document.getElementById("loading-animation"); + + if (isLoading) { + // Show loading animation + elLoading.classList.remove("hidden"); + } else { + // Hide loading animation + elLoading.classList.add("hidden"); + } +} + +function uiStatusError(message, showLoadingAnimation) { + uiToggleLoadingAnimation(showLoadingAnimation); + + const elStatus = document.getElementById("status"); + const elControls = document.getElementById("controls"); + + // Show status error + elStatus.classList.remove("success"); + elStatus.classList.remove("inactive"); + elStatus.classList.add("error"); + elStatus.innerText = message; + + // Hide controls + elControls.classList.add("hidden"); +} + +function makeErrorMsg(errorObj) { + return "Error\n" + errorObj.code + "\n" + errorObj.message; +} + +// -------------- // +// LIFF functions // +// -------------- // + +function initializeApp() { + liff.init(() => initializeLiff(), error => uiStatusError(makeErrorMsg(error), false)); +} + +function initializeLiff() { + liff.initPlugins(['bluetooth']).then(() => { + liffCheckAvailablityAndDo(() => liffRequestDevice()); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} + +function liffCheckAvailablityAndDo(callbackIfAvailable) { + // Check Bluetooth availability + liff.bluetooth.getAvailability().then(isAvailable => { + if (isAvailable) { + uiToggleDeviceConnected(false); + callbackIfAvailable(); + } else { + uiStatusError("Bluetooth not available", true); + setTimeout(() => liffCheckAvailablityAndDo(callbackIfAvailable), 10000); + } + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + });; +} + +function liffRequestDevice() { + liff.bluetooth.requestDevice().then(device => { + liffConnectToDevice(device); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} + +function liffConnectToDevice(device) { + device.gatt.connect().then(() => { + document.getElementById("device-name").innerText = device.name; + document.getElementById("device-id").innerText = device.id; + + // Show status connected + uiToggleDeviceConnected(true); + + // Get service + device.gatt.getPrimaryService(USER_SERVICE_UUID).then(service => { + liffGetUserService(service); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); + device.gatt.getPrimaryService(PSDI_SERVICE_UUID).then(service => { + liffGetPSDIService(service); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); + + // Device disconnect callback + const disconnectCallback = () => { + // Show status disconnected + uiToggleDeviceConnected(false); + + // Remove disconnect callback + device.removeEventListener('gattserverdisconnected', disconnectCallback); + + // Reset LED state + ledState = false; + // Reset UI elements + uiToggleLedButton(false); + uiToggleStateButton(false); + + // Try to reconnect + initializeLiff(); + }; + + device.addEventListener('gattserverdisconnected', disconnectCallback); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} + +function liffGetUserService(service) { + // Button pressed state + service.getCharacteristic(BTN_CHARACTERISTIC_UUID).then(characteristic => { + liffGetButtonStateCharacteristic(characteristic); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); + + // Toggle LED + service.getCharacteristic(LED_CHARACTERISTIC_UUID).then(characteristic => { + window.ledCharacteristic = characteristic; + + // Switch off by default + liffToggleDeviceLedState(false); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} + +function liffGetPSDIService(service) { + // Get PSDI value + service.getCharacteristic(PSDI_CHARACTERISTIC_UUID).then(characteristic => { + return characteristic.readValue(); + }).then(value => { + // Byte array to hex string + const psdi = new Uint8Array(value.buffer) + .reduce((output, byte) => output + ("0" + byte.toString(16)).slice(-2), ""); + document.getElementById("device-psdi").innerText = psdi; + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} + +function liffGetButtonStateCharacteristic(characteristic) { + // Add notification hook for button state + // (Get notified when button state changes) + characteristic.startNotifications().then(() => { + characteristic.addEventListener('characteristicvaluechanged', e => { + const val = (new Uint8Array(e.target.value.buffer))[0]; + if (val > 0) { + // press + uiToggleStateButton(true); + } else { + // release + uiToggleStateButton(false); + uiCountPressButton(); + } + }); + }).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} + +function liffToggleDeviceLedState(state) { + // on: 0x01 + // off: 0x00 + window.ledCharacteristic.writeValue( + state ? new Uint8Array([0x01]) : new Uint8Array([0x00]) + ).catch(error => { + uiStatusError(makeErrorMsg(error), false); + }); +} From 9135079e62a0771c05dddb7a5a365d02748e3ede Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:37:01 +0700 Subject: [PATCH 012/213] Delete liff.js --- liff.js | 271 -------------------------------------------------------- 1 file changed, 271 deletions(-) delete mode 100644 liff.js diff --git a/liff.js b/liff.js deleted file mode 100644 index 2adb7ac..0000000 --- a/liff.js +++ /dev/null @@ -1,271 +0,0 @@ -// User service UUID: Change this to your generated service UUID -const USER_SERVICE_UUID = '91E4E176-D0B9-464D-9FE4-52EE3E9F1552'; // LED, Button -// User service characteristics -const LED_CHARACTERISTIC_UUID = 'E9062E71-9E62-4BC6-B0D3-35CDCD9B027B'; -const BTN_CHARACTERISTIC_UUID = '62FBD229-6EDD-4D1A-B554-5C4E1BB29169'; - -// PSDI Service UUID: Fixed value for Developer Trial -const PSDI_SERVICE_UUID = 'E625601E-9E55-4597-A598-76018A0D293D'; // Device ID -const PSDI_CHARACTERISTIC_UUID = '26E2B12B-85F0-4F3F-9FDD-91D114270E6E'; - -// UI settings -let ledState = false; // true: LED on, false: LED off -let clickCount = 0; - -// -------------- // -// On window load // -// -------------- // - -window.onload = () => { - initializeApp(); -}; - -// ----------------- // -// Handler functions // -// ----------------- // - -function handlerToggleLed() { - ledState = !ledState; - - uiToggleLedButton(ledState); - liffToggleDeviceLedState(ledState); -} - -// ------------ // -// UI functions // -// ------------ // - -function uiToggleLedButton(state) { - const el = document.getElementById("btn-led-toggle"); - el.innerText = state ? "Switch LED OFF" : "Switch LED ON"; - - if (state) { - el.classList.add("led-on"); - } else { - el.classList.remove("led-on"); - } -} - -function uiCountPressButton() { - clickCount++; - - const el = document.getElementById("click-count"); - el.innerText = clickCount; -} - -function uiToggleStateButton(pressed) { - const el = document.getElementById("btn-state"); - - if (pressed) { - el.classList.add("pressed"); - el.innerText = "Pressed"; - } else { - el.classList.remove("pressed"); - el.innerText = "Released"; - } -} - -function uiToggleDeviceConnected(connected) { - const elStatus = document.getElementById("status"); - const elControls = document.getElementById("controls"); - - elStatus.classList.remove("error"); - - if (connected) { - // Hide loading animation - uiToggleLoadingAnimation(false); - // Show status connected - elStatus.classList.remove("inactive"); - elStatus.classList.add("success"); - elStatus.innerText = "Device connected"; - // Show controls - elControls.classList.remove("hidden"); - } else { - // Show loading animation - uiToggleLoadingAnimation(true); - // Show status disconnected - elStatus.classList.remove("success"); - elStatus.classList.add("inactive"); - elStatus.innerText = "Device disconnected"; - // Hide controls - elControls.classList.add("hidden"); - } -} - -function uiToggleLoadingAnimation(isLoading) { - const elLoading = document.getElementById("loading-animation"); - - if (isLoading) { - // Show loading animation - elLoading.classList.remove("hidden"); - } else { - // Hide loading animation - elLoading.classList.add("hidden"); - } -} - -function uiStatusError(message, showLoadingAnimation) { - uiToggleLoadingAnimation(showLoadingAnimation); - - const elStatus = document.getElementById("status"); - const elControls = document.getElementById("controls"); - - // Show status error - elStatus.classList.remove("success"); - elStatus.classList.remove("inactive"); - elStatus.classList.add("error"); - elStatus.innerText = message; - - // Hide controls - elControls.classList.add("hidden"); -} - -function makeErrorMsg(errorObj) { - return "Error\n" + errorObj.code + "\n" + errorObj.message; -} - -// -------------- // -// LIFF functions // -// -------------- // - -function initializeApp() { - liff.init(() => initializeLiff(), error => uiStatusError(makeErrorMsg(error), false)); -} - -function initializeLiff() { - liff.initPlugins(['bluetooth']).then(() => { - liffCheckAvailablityAndDo(() => liffRequestDevice()); - }).catch(error => { - uiStatusError(makeErrorMsg(error), false); - }); -} - -function liffCheckAvailablityAndDo(callbackIfAvailable) { - // Check Bluetooth availability - liff.bluetooth.getAvailability().then(isAvailable => { - if (isAvailable) { - uiToggleDeviceConnected(false); - callbackIfAvailable(); - } else { - uiStatusError("Bluetooth not available", true); - setTimeout(() => liffCheckAvailablityAndDo(callbackIfAvailable), 10000); - } - }).catch(error => { - uiStatusError(makeErrorMsg(error), false); - });; -} - -function liffRequestDevice() { - liff.bluetooth.requestDevice().then(device => { - liffConnectToDevice(device); - }).catch(error => { - uiStatusError(makeErrorMsg(error), false); - }); -} - -function liffConnectToDevice(device) { - device.gatt.connect().then(() => { - document.getElementById("device-name").innerText = device.name; - document.getElementById("device-id").innerText = device.id; - - // Show status connected - uiToggleDeviceConnected(true); - - // Get service - device.gatt.getPrimaryService(USER_SERVICE_UUID).then(service => { - liffGetUserService(service); - }).catch(error => { - uiStatusError(makeErrorMsg(error), false); - }); - device.gatt.getPrimaryService(PSDI_SERVICE_UUID).then(service => { - liffGetPSDIService(service); - }).catch(error => { - uiStatusError(makeErrorMsg(error), false); - }); - - // Device disconnect callback - const disconnectCallback = () => { - // Show status disconnected - uiToggleDeviceConnected(false); - - // Remove disconnect callback - device.removeEventListener('gattserverdisconnected', disconnectCallback); - - // Reset LED state - ledState = false; - // Reset UI elements - uiToggleLedButton(false); - uiToggleStateButton(false); - - // Try to reconnect - initializeLiff(); - }; - - device.addEventListener('gattserverdisconnected', disconnectCallback); - }).catch(error => { - uiStatusError(makeErrorMsg(error), false); - }); -} - -function liffGetUserService(service) { - // Button pressed state - service.getCharacteristic(BTN_CHARACTERISTIC_UUID).then(characteristic => { - liffGetButtonStateCharacteristic(characteristic); - }).catch(error => { - uiStatusError(makeErrorMsg(error), false); - }); - - // Toggle LED - service.getCharacteristic(LED_CHARACTERISTIC_UUID).then(characteristic => { - window.ledCharacteristic = characteristic; - - // Switch off by default - liffToggleDeviceLedState(false); - }).catch(error => { - uiStatusError(makeErrorMsg(error), false); - }); -} - -function liffGetPSDIService(service) { - // Get PSDI value - service.getCharacteristic(PSDI_CHARACTERISTIC_UUID).then(characteristic => { - return characteristic.readValue(); - }).then(value => { - // Byte array to hex string - const psdi = new Uint8Array(value.buffer) - .reduce((output, byte) => output + ("0" + byte.toString(16)).slice(-2), ""); - document.getElementById("device-psdi").innerText = psdi; - }).catch(error => { - uiStatusError(makeErrorMsg(error), false); - }); -} - -function liffGetButtonStateCharacteristic(characteristic) { - // Add notification hook for button state - // (Get notified when button state changes) - characteristic.startNotifications().then(() => { - characteristic.addEventListener('characteristicvaluechanged', e => { - const val = (new Uint8Array(e.target.value.buffer))[0]; - if (val > 0) { - // press - uiToggleStateButton(true); - } else { - // release - uiToggleStateButton(false); - uiCountPressButton(); - } - }); - }).catch(error => { - uiStatusError(makeErrorMsg(error), false); - }); -} - -function liffToggleDeviceLedState(state) { - // on: 0x01 - // off: 0x00 - window.ledCharacteristic.writeValue( - state ? new Uint8Array([0x01]) : new Uint8Array([0x00]) - ).catch(error => { - uiStatusError(makeErrorMsg(error), false); - }); -} From bf52d13df55e7f320884f687d6c01d000e51b435 Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:38:13 +0700 Subject: [PATCH 013/213] Add files via upload --- .../liff-starter.js" | 88 +++++++++++++++++ .../liff.css" | 97 +++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 "\340\271\201\340\270\255\340\270\233 liff/liff-starter.js" create mode 100644 "\340\271\201\340\270\255\340\270\233 liff/liff.css" diff --git "a/\340\271\201\340\270\255\340\270\233 liff/liff-starter.js" "b/\340\271\201\340\270\255\340\270\233 liff/liff-starter.js" new file mode 100644 index 0000000..09454ee --- /dev/null +++ "b/\340\271\201\340\270\255\340\270\233 liff/liff-starter.js" @@ -0,0 +1,88 @@ +window.onload = function (e) { + liff.init(function (data) { + initializeApp(data); + }); +}; + +function initializeApp(data) { + document.getElementById('languagefield').textContent = data.language; + document.getElementById('viewtypefield').textContent = data.context.viewType; + document.getElementById('useridfield').textContent = data.context.userId; + document.getElementById('utouidfield').textContent = data.context.utouId; + document.getElementById('roomidfield').textContent = data.context.roomId; + document.getElementById('groupidfield').textContent = data.context.groupId; + + // openWindow call + document.getElementById('openwindowbutton').addEventListener('click', function () { + liff.openWindow({ + url: 'https://line.me' + }); + }); + + // closeWindow call + document.getElementById('closewindowbutton').addEventListener('click', function () { + liff.closeWindow(); + }); + + // sendMessages call + document.getElementById('sendmessagebutton').addEventListener('click', function () { + liff.sendMessages([{ + type: 'text', + text: "You've successfully sent a message! Hooray!" + }, { + type: 'sticker', + packageId: '2', + stickerId: '144' + }]).then(function () { + window.alert("Message sent"); + }).catch(function (error) { + window.alert("Error sending message: " + error); + }); + }); + + // get access token + document.getElementById('getaccesstoken').addEventListener('click', function () { + const accessToken = liff.getAccessToken(); + document.getElementById('accesstokenfield').textContent = accessToken; + toggleAccessToken(); + }); + + // get profile call + document.getElementById('getprofilebutton').addEventListener('click', function () { + liff.getProfile().then(function (profile) { + document.getElementById('useridprofilefield').textContent = profile.userId; + document.getElementById('displaynamefield').textContent = profile.displayName; + + const profilePictureDiv = document.getElementById('profilepicturediv'); + if (profilePictureDiv.firstElementChild) { + profilePictureDiv.removeChild(profilePictureDiv.firstElementChild); + } + const img = document.createElement('img'); + img.src = profile.pictureUrl; + img.alt = "Profile Picture"; + profilePictureDiv.appendChild(img); + + document.getElementById('statusmessagefield').textContent = profile.statusMessage; + toggleProfileData(); + }).catch(function (error) { + window.alert("Error getting profile: " + error); + }); + }); +} + +function toggleAccessToken() { + toggleElement('accesstokendata'); +} + +function toggleProfileData() { + toggleElement('profileinfo'); +} + +function toggleElement(elementId) { + const elem = document.getElementById(elementId); + if (elem.offsetWidth > 0 && elem.offsetHeight > 0) { + elem.style.display = "none"; + } else { + elem.style.display = "block"; + } +} \ No newline at end of file diff --git "a/\340\271\201\340\270\255\340\270\233 liff/liff.css" "b/\340\271\201\340\270\255\340\270\233 liff/liff.css" new file mode 100644 index 0000000..217f202 --- /dev/null +++ "b/\340\271\201\340\270\255\340\270\233 liff/liff.css" @@ -0,0 +1,97 @@ +body, table { + font-size: 14pt; + font-family: "Montserrat", sans-serif; + margin: 0pt; + text-align: center; + padding-top: 18pt; +} + +p { + padding: 0pt 14pt; +} + +.btn { + padding: 2pt; + font-size: 14pt; + text-align: center; + text-decoration: none; + display: inline-block; +} + +.btn-led-toggle { + box-shadow: 0 4pt 8pt 0 rgba(0,0,0,0.2), 0 3pt 10pt 0 rgba(0,0,0,0.19); + color: #f8f9fa; /* Light */ + background-color: #6c757d; /* Gray */ + width: 160pt; + font-weight: bold; + height: 40pt; + border: 2pt solid black; + border-radius: 8pt; +} + +.btn-led-toggle:active { + transform: translateY(4pt); + box-shadow: 0 2pt 4pt 0 rgba(0,0,0,0.2), 0 1pt 5pt 0 rgba(0,0,0,0.19); + background-color: #6c757d; /* Gray */ +} + +.led-on, .led-on:active { + background-color: #16C464; /* LINE Green */ +} + +.btn-state { + box-shadow: 0pt 6pt #999; + border-radius: 4pt; + margin-bottom: 8pt; + width: 100pt; + border: 1pt solid #6c757d; /* Gray */ + color: #6c757d; /* Gray */ + background-color: #f8f9fa; /* Light */ +} + +.btn-state:active, .pressed { + color: #343a40; /* Dark */ + border: 1pt solid #343a40; /* Dark */ + box-shadow: 0 2pt #666; + transform: translateY(4pt); +} + +.inactive { + color: #6c757d; /* Gray */ +} + +.success { + color: #16C464; /* LINE Green */ +} + +.error { + color: #dc3545; /* Red */ +} + +.hidden { + display: none; +} + +.device-info-table { + width: 80%; + margin: auto; + padding: 0; +} + +.device-info-cell { + text-align: left; + vertical-align: middle; + padding-bottom: 8px; +} + +.device-info-key { + padding-right: 10px; + word-wrap: none; +} + +.device-info-val { + padding-left: 10px; + word-wrap: break-word; + max-width: 200px; + font-family: monospace, monospace; +} From b5b41de01e2a0b01e418bd451a615f8c246c506f Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:40:42 +0700 Subject: [PATCH 014/213] Add files via upload --- .../app.json" | 5 + .../game.html" | 23 + .../game.js" | 478 ++++++++++++++++++ .../index.php" | 2 + 4 files changed, 508 insertions(+) create mode 100644 "\340\271\201\340\270\255\340\270\233 liff/app.json" create mode 100644 "\340\271\201\340\270\255\340\270\233 liff/game.html" create mode 100644 "\340\271\201\340\270\255\340\270\233 liff/game.js" create mode 100644 "\340\271\201\340\270\255\340\270\233 liff/index.php" diff --git "a/\340\271\201\340\270\255\340\270\233 liff/app.json" "b/\340\271\201\340\270\255\340\270\233 liff/app.json" new file mode 100644 index 0000000..2b5edda --- /dev/null +++ "b/\340\271\201\340\270\255\340\270\233 liff/app.json" @@ -0,0 +1,5 @@ +{ + "name": "LINE Liff Starter", + "description": "Sample LINE LIFF application", + "repository": "https://github.com/line/line-liff-starter" +} \ No newline at end of file diff --git "a/\340\271\201\340\270\255\340\270\233 liff/game.html" "b/\340\271\201\340\270\255\340\270\233 liff/game.html" new file mode 100644 index 0000000..e98f34f --- /dev/null +++ "b/\340\271\201\340\270\255\340\270\233 liff/game.html" @@ -0,0 +1,23 @@ + + + + + + + + Bird Demo + + + + + + + + + diff --git "a/\340\271\201\340\270\255\340\270\233 liff/game.js" "b/\340\271\201\340\270\255\340\270\233 liff/game.js" new file mode 100644 index 0000000..d7118b9 --- /dev/null +++ "b/\340\271\201\340\270\255\340\270\233 liff/game.js" @@ -0,0 +1,478 @@ +// User service UUID: Change this to your generated service UUID +const USER_SERVICE_UUID = 'c1a74048-c5c8-4e96-9720-ab28c66f12c0'; +const BTN_CHARACTERISTIC_UUID = '62FBD229-6EDD-4D1A-B554-5C4E1BB29169'; +const SCORE_CHARACTERISTIC_UUID = 'E9062E71-9E62-4BC6-B0D3-35CDCD9B027B'; +// PSDI Service UUID: Fixed value for Developer Trial +const PSDI_SERVICE_UUID = 'E625601E-9E55-4597-A598-76018A0D293D'; // Device ID +const PSDI_CHARACTERISTIC_UUID = '26E2B12B-85F0-4F3F-9FDD-91D114270E6E'; + +// GAME STATE +const state = { + current : 0, + getReady : 0, + game : 1, + over : 2 +} + +window.onload = () => { + console.log("JS Ver 2") + var scale = 'scale(2.5)'; + elcanvas = document.getElementById("bird"); + elcanvas.style.webkitTransform = scale; + elcanvas.style.transform = scale; + initializeApp(); +}; + +function initializeApp() { + liff.init(() => initializeLiff(), error => console.log("error")); +} + +function initializeLiff() { + liff.initPlugins(['bluetooth']).then(() => { + liffCheckAvailablityAndDo(() => liffRequestDevice()); + }).catch(error => { + }); +} + +function liffCheckAvailablityAndDo(callbackIfAvailable) { + liff.bluetooth.getAvailability().then(isAvailable => { + if (isAvailable) { + callbackIfAvailable(); + } else { + setTimeout(() => liffCheckAvailablityAndDo(callbackIfAvailable), 10000); + } + }).catch(error => { + });; +} + +function liffRequestDevice() { + console.log("requesting"); + liff.bluetooth.requestDevice().then(device => { + console.log("connecting"); + liffConnectToDevice(device); + }).catch(error => { + }); +} + +function liffConnectToDevice(device) { + device.gatt.connect().then(() => { + console.log("connected to " + device.name) + + device.gatt.getPrimaryService(USER_SERVICE_UUID).then(service => { + liffGetUserService(service); + }).catch(error => { + }); + + device.gatt.getPrimaryService(PSDI_SERVICE_UUID).then(service => { + liffGetPSDIService(service); + }).catch(error => { + }); + + // Device disconnect callback + const disconnectCallback = () => { + // Remove disconnect callback + device.removeEventListener('gattserverdisconnected', disconnectCallback); + + // Try to reconnect + initializeLiff(); + }; + + loop(); // Start Game + + device.addEventListener('gattserverdisconnected', disconnectCallback); + }).catch(error => { + + }); +} + +function liffGetUserService(service) { + service.getCharacteristic(BTN_CHARACTERISTIC_UUID).then(characteristic => { + liffGetButtonStateCharacteristic(characteristic); + }).catch(error => { + }); + + service.getCharacteristic(SCORE_CHARACTERISTIC_UUID).then(characteristic => { + window.scoreCharacteristic = characteristic; + }).catch(error => { + }); +} + +function liffGetPSDIService(service) { + // Get PSDI value + service.getCharacteristic(PSDI_CHARACTERISTIC_UUID).then(characteristic => { + return characteristic.readValue(); + }).then(value => { + // Byte array to hex string + const psdi = new Uint8Array(value.buffer) + .reduce((output, byte) => output + ("0" + byte.toString(16)).slice(-2), ""); + }).catch(error => { + }); +} + +function liffGetButtonStateCharacteristic(characteristic) { + // Add notification hook for button state + // (Get notified when button state changes) + characteristic.startNotifications().then(() => { + console.log("Starting Notifications") + characteristic.addEventListener('characteristicvaluechanged', e => { + const val = (new Uint8Array(e.target.value.buffer))[0]; + if (val > 0) { + switch(state.current){ + case state.getReady: + state.current = state.game; + SWOOSHING.play(); + break; + case state.game: + if(bird.y - bird.radius <= 0) return; + bird.flap(); + FLAP.play(); + break; + case state.over: + pipes.reset(); + bird.speedReset(); + score.reset(); + state.current = state.getReady; + break; + } + } else { + } + }); + }).catch(error => { + }); +} + +function liffWriteScore(sendScore) { + if(sendScore < 100){ + window.scoreCharacteristic.writeValue( + new Uint8Array([sendScore]) + ).catch(error => { + }); + } +} + +// SELECT CVS +const cvs = document.getElementById("bird"); +const ctx = cvs.getContext("2d"); + +// GAME VARS AND CONSTS +let frames = 0; +const DEGREE = Math.PI/180; + +// LOAD SPRITE IMAGE +const sprite = new Image(); +sprite.src = "img/sprite.png"; + +// LOAD SOUNDS +const SCORE_S = new Audio(); +SCORE_S.src = "audio/sfx_point.wav"; + +const FLAP = new Audio(); +FLAP.src = "audio/sfx_flap.wav"; + +const HIT = new Audio(); +HIT.src = "audio/sfx_hit.wav"; + +const SWOOSHING = new Audio(); +SWOOSHING.src = "audio/sfx_swooshing.wav"; + +const DIE = new Audio(); +DIE.src = "audio/sfx_die.wav"; + +// START BUTTON COORD +const startBtn = { + x : 120, + y : 263, + w : 83, + h : 29 +} + +// BACKGROUND +const bg = { + sX : 0, + sY : 0, + w : 275, + h : 226, + x : 0, + y : cvs.height - 226, + + draw : function(){ + ctx.drawImage(sprite, this.sX, this.sY, this.w, this.h, this.x, this.y, this.w, this.h); + + ctx.drawImage(sprite, this.sX, this.sY, this.w, this.h, this.x + this.w, this.y, this.w, this.h); + } +} + +// FOREGROUND +const fg = { + sX: 276, + sY: 0, + w: 224, + h: 112, + x: 0, + y: cvs.height - 112, + + dx : 2, + + draw : function(){ + ctx.drawImage(sprite, this.sX, this.sY, this.w, this.h, this.x, this.y, this.w, this.h); + + ctx.drawImage(sprite, this.sX, this.sY, this.w, this.h, this.x + this.w, this.y, this.w, this.h); + }, + + update: function(){ + if(state.current == state.game){ + this.x = (this.x - this.dx)%(this.w/2); + } + } +} + +// BIRD +const bird = { + animation : [ + {sX: 276, sY : 112}, + {sX: 276, sY : 139}, + {sX: 276, sY : 164}, + {sX: 276, sY : 139} + ], + x : 50, + y : 150, + w : 34, + h : 26, + + radius : 12, + + frame : 0, + + gravity : 0.25, + jump : 4.6, + speed : 0, + rotation : 0, + + draw : function(){ + let bird = this.animation[this.frame]; + + ctx.save(); + ctx.translate(this.x, this.y); + ctx.rotate(this.rotation); + ctx.drawImage(sprite, bird.sX, bird.sY, this.w, this.h,- this.w/2, - this.h/2, this.w, this.h); + + ctx.restore(); + }, + + flap : function(){ + this.speed = - this.jump; + }, + + update: function(){ + // IF THE GAME STATE IS GET READY STATE, THE BIRD MUST FLAP SLOWLY + this.period = state.current == state.getReady ? 10 : 5; + // WE INCREMENT THE FRAME BY 1, EACH PERIOD + this.frame += frames%this.period == 0 ? 1 : 0; + // FRAME GOES FROM 0 To 4, THEN AGAIN TO 0 + this.frame = this.frame%this.animation.length; + + if(state.current == state.getReady){ + this.y = 150; // RESET POSITION OF THE BIRD AFTER GAME OVER + this.rotation = 0 * DEGREE; + }else{ + this.speed += this.gravity; + this.y += this.speed; + + if(this.y + this.h/2 >= cvs.height - fg.h){ + this.y = cvs.height - fg.h - this.h/2; + if(state.current == state.game){ + state.current = state.over; + DIE.play(); + } + } + + // IF THE SPEED IS GREATER THAN THE JUMP MEANS THE BIRD IS FALLING DOWN + if(this.speed >= this.jump){ + this.rotation = 90 * DEGREE; + this.frame = 1; + }else{ + this.rotation = -25 * DEGREE; + } + } + }, + speedReset : function(){ + this.speed = 0; + } +} + +// GET READY MESSAGE +const getReady = { + sX : 0, + sY : 228, + w : 173, + h : 152, + x : cvs.width/2 - 173/2, + y : 80, + + draw: function(){ + if(state.current == state.getReady){ + ctx.drawImage(sprite, this.sX, this.sY, this.w, this.h, this.x, this.y, this.w, this.h); + } + } + +} + +// GAME OVER MESSAGE +const gameOver = { + sX : 175, + sY : 228, + w : 225, + h : 202, + x : cvs.width/2 - 225/2, + y : 90, + + draw: function(){ + if(state.current == state.over){ + ctx.drawImage(sprite, this.sX, this.sY, this.w, this.h, this.x, this.y, this.w, this.h); + } + } +} + +// PIPES +const pipes = { + position : [], + + top : { + sX : 553, + sY : 0 + }, + bottom:{ + sX : 502, + sY : 0 + }, + + w : 53, + h : 400, + gap : 85, + maxYPos : -150, + dx : 2, + + draw : function(){ + for(let i = 0; i < this.position.length; i++){ + let p = this.position[i]; + + let topYPos = p.y; + let bottomYPos = p.y + this.h + this.gap; + + // top pipe + ctx.drawImage(sprite, this.top.sX, this.top.sY, this.w, this.h, p.x, topYPos, this.w, this.h); + + // bottom pipe + ctx.drawImage(sprite, this.bottom.sX, this.bottom.sY, this.w, this.h, p.x, bottomYPos, this.w, this.h); + } + }, + + update: function(){ + if(state.current !== state.game) return; + + if(frames%100 == 0){ + this.position.push({ + x : cvs.width, + y : this.maxYPos * ( Math.random() + 1) + }); + } + for(let i = 0; i < this.position.length; i++){ + let p = this.position[i]; + + let bottomPipeYPos = p.y + this.h + this.gap; + + // COLLISION DETECTION + // TOP PIPE + if(bird.x + bird.radius > p.x && bird.x - bird.radius < p.x + this.w && bird.y + bird.radius > p.y && bird.y - bird.radius < p.y + this.h){ + state.current = state.over; + HIT.play(); + } + // BOTTOM PIPE + if(bird.x + bird.radius > p.x && bird.x - bird.radius < p.x + this.w && bird.y + bird.radius > bottomPipeYPos && bird.y - bird.radius < bottomPipeYPos + this.h){ + state.current = state.over; + HIT.play(); + } + + // MOVE THE PIPES TO THE LEFT + p.x -= this.dx; + + // if the pipes go beyond canvas, we delete them from the array + if(p.x + this.w <= 0){ + this.position.shift(); + score.value += 1; + SCORE_S.play(); + score.best = Math.max(score.value, score.best); + localStorage.setItem("best", score.best); + } + } + }, + + reset : function(){ + this.position = []; + } + +} + +// SCORE +const score= { + best : parseInt(localStorage.getItem("best")) || 0, + value : 0, + + draw : function(){ + ctx.fillStyle = "#FFF"; + ctx.strokeStyle = "#000"; + + if(state.current == state.game){ + ctx.lineWidth = 2; + ctx.font = "35px Teko"; + ctx.fillText(this.value, cvs.width/2, 50); + ctx.strokeText(this.value, cvs.width/2, 50); + + }else if(state.current == state.over){ + // SCORE VALUE + ctx.font = "25px Teko"; + ctx.fillText(this.value, 225, 186); + ctx.strokeText(this.value, 225, 186); + + // BEST SCORE + ctx.fillText(this.best, 225, 228); + ctx.strokeText(this.best, 225, 228); + } + }, + + reset : function(){ + liffWriteScore(0x00+parseInt(this.value)); + this.value = 0; + } +} + +// DRAW +function draw(){ + ctx.fillStyle = "#70c5ce"; + ctx.fillRect(0, 0, cvs.width, cvs.height); + + bg.draw(); + pipes.draw(); + fg.draw(); + bird.draw(); + getReady.draw(); + gameOver.draw(); + score.draw(); +} + +// UPDATE +function update(){ + bird.update(); + fg.update(); + pipes.update(); +} + +// LOOP +function loop(){ + update(); + draw(); + frames++; + + requestAnimationFrame(loop); +} +// loop(); diff --git "a/\340\271\201\340\270\255\340\270\233 liff/index.php" "b/\340\271\201\340\270\255\340\270\233 liff/index.php" new file mode 100644 index 0000000..a39bd6a --- /dev/null +++ "b/\340\271\201\340\270\255\340\270\233 liff/index.php" @@ -0,0 +1,2 @@ + + \ No newline at end of file From 6995fc8c685acd5d33399a9efe9a568ebf61ff1b Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:41:01 +0700 Subject: [PATCH 015/213] Delete app.json --- app.json | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 app.json diff --git a/app.json b/app.json deleted file mode 100644 index 2b5edda..0000000 --- a/app.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "LINE Liff Starter", - "description": "Sample LINE LIFF application", - "repository": "https://github.com/line/line-liff-starter" -} \ No newline at end of file From cc764b7cc534dc1b4aac63dcce886b007b2bcff2 Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:41:19 +0700 Subject: [PATCH 016/213] Delete game.html --- game.html | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 game.html diff --git a/game.html b/game.html deleted file mode 100644 index e98f34f..0000000 --- a/game.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - Bird Demo - - - - - - - - - From 30e7ac45187b795748b37471eff7dd765683f3b2 Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:41:50 +0700 Subject: [PATCH 017/213] Delete liff-starter.js --- liff-starter.js | 88 ------------------------------------------------- 1 file changed, 88 deletions(-) delete mode 100644 liff-starter.js diff --git a/liff-starter.js b/liff-starter.js deleted file mode 100644 index 09454ee..0000000 --- a/liff-starter.js +++ /dev/null @@ -1,88 +0,0 @@ -window.onload = function (e) { - liff.init(function (data) { - initializeApp(data); - }); -}; - -function initializeApp(data) { - document.getElementById('languagefield').textContent = data.language; - document.getElementById('viewtypefield').textContent = data.context.viewType; - document.getElementById('useridfield').textContent = data.context.userId; - document.getElementById('utouidfield').textContent = data.context.utouId; - document.getElementById('roomidfield').textContent = data.context.roomId; - document.getElementById('groupidfield').textContent = data.context.groupId; - - // openWindow call - document.getElementById('openwindowbutton').addEventListener('click', function () { - liff.openWindow({ - url: 'https://line.me' - }); - }); - - // closeWindow call - document.getElementById('closewindowbutton').addEventListener('click', function () { - liff.closeWindow(); - }); - - // sendMessages call - document.getElementById('sendmessagebutton').addEventListener('click', function () { - liff.sendMessages([{ - type: 'text', - text: "You've successfully sent a message! Hooray!" - }, { - type: 'sticker', - packageId: '2', - stickerId: '144' - }]).then(function () { - window.alert("Message sent"); - }).catch(function (error) { - window.alert("Error sending message: " + error); - }); - }); - - // get access token - document.getElementById('getaccesstoken').addEventListener('click', function () { - const accessToken = liff.getAccessToken(); - document.getElementById('accesstokenfield').textContent = accessToken; - toggleAccessToken(); - }); - - // get profile call - document.getElementById('getprofilebutton').addEventListener('click', function () { - liff.getProfile().then(function (profile) { - document.getElementById('useridprofilefield').textContent = profile.userId; - document.getElementById('displaynamefield').textContent = profile.displayName; - - const profilePictureDiv = document.getElementById('profilepicturediv'); - if (profilePictureDiv.firstElementChild) { - profilePictureDiv.removeChild(profilePictureDiv.firstElementChild); - } - const img = document.createElement('img'); - img.src = profile.pictureUrl; - img.alt = "Profile Picture"; - profilePictureDiv.appendChild(img); - - document.getElementById('statusmessagefield').textContent = profile.statusMessage; - toggleProfileData(); - }).catch(function (error) { - window.alert("Error getting profile: " + error); - }); - }); -} - -function toggleAccessToken() { - toggleElement('accesstokendata'); -} - -function toggleProfileData() { - toggleElement('profileinfo'); -} - -function toggleElement(elementId) { - const elem = document.getElementById(elementId); - if (elem.offsetWidth > 0 && elem.offsetHeight > 0) { - elem.style.display = "none"; - } else { - elem.style.display = "block"; - } -} \ No newline at end of file From 2ce61c1f7592d12cd14a02359c2ac5243d1c1b3a Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:42:49 +0700 Subject: [PATCH 018/213] Delete index.js --- index.js | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 index.js diff --git a/index.js b/index.js deleted file mode 100644 index 723a82e..0000000 --- a/index.js +++ /dev/null @@ -1,15 +0,0 @@ -const core = require('@actions/core'); -const github = require('@actions/github'); - -try { - // `who-to-greet` input defined in action metadata file - const nameToGreet = core.getInput('who-to-greet'); - console.log(`Hello ${nameToGreet}!`); - const time = (new Date()).toTimeString(); - core.setOutput("time", time); - // Get the JSON webhook payload for the event that triggered the workflow - const payload = JSON.stringify(github.context.payload, undefined, 2) - console.log(`The event payload: ${payload}`); -} catch (error) { - core.setFailed(error.message); -} From ecbe36db728f4084c0e5315b42527d9f2cd7dbde Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:48:23 +0700 Subject: [PATCH 019/213] Create index.js --- .../index.js" | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 "\340\271\201\340\270\255\340\270\233 liff/index.js" diff --git "a/\340\271\201\340\270\255\340\270\233 liff/index.js" "b/\340\271\201\340\270\255\340\270\233 liff/index.js" new file mode 100644 index 0000000..b5e1513 --- /dev/null +++ "b/\340\271\201\340\270\255\340\270\233 liff/index.js" @@ -0,0 +1,15 @@ +const core = ต้องการ ('@ actions / core'); +const github = ต้องการ ('@ actions / github'); + +ลอง { + // อินพุต `who-to-greet` ถูกกำหนดในไฟล์ข้อมูลเมตาของแอ็คชัน + const nameToGreet = core.getInput ('who-to-greet'); + console.log (`สวัสดี $ {nameToGreet}! '); + เวลา const = (ใหม่วันที่ ()) toTimeString (); + core.setOutput ("เวลา" เวลา) + // รับ JSON webhook payload สำหรับเหตุการณ์ที่เรียกใช้เวิร์กโฟลว์ + const payload = JSON.stringify (github.context.payload, ไม่ได้กำหนด, 2) + console.log (`ส่วนของเหตุการณ์: $ {payload}`); +} catch (ข้อผิดพลาด) { + core.setFailed (error.message); +} From e2ebe470aa715e5f17f6be599feecfa4934ae8ca Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:49:07 +0700 Subject: [PATCH 020/213] Delete index.php --- index.php | 1 - 1 file changed, 1 deletion(-) delete mode 100644 index.php diff --git a/index.php b/index.php deleted file mode 100644 index b0e04b7..0000000 --- a/index.php +++ /dev/null @@ -1 +0,0 @@ - From c58f9b96afac4898c5c46026808def193cb6ccf3 Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:50:27 +0700 Subject: [PATCH 021/213] Create index.html --- .../index.html" | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 "\340\271\201\340\270\255\340\270\233 liff/index.html" diff --git "a/\340\271\201\340\270\255\340\270\233 liff/index.html" "b/\340\271\201\340\270\255\340\270\233 liff/index.html" new file mode 100644 index 0000000..186175a --- /dev/null +++ "b/\340\271\201\340\270\255\340\270\233 liff/index.html" @@ -0,0 +1,24 @@ + + + ꧁💓 @:꓄ꍏꈤᖘꍏ꓄꓄ꍏꌩ💓꧂BOTS LIFF-APP + + + + +
+
+ HELLO !! + +
+

+

+

+

+

+
+

꧁💓 @:꓄ꍏꈤᖘꍏ꓄꓄ꍏꌩ💓꧂BOTS LIFF-APP + + + + + From 5d2ed75ca65856c1620158529e5ca087f43ca95c Mon Sep 17 00:00:00 2001 From: Pratan naimjoi <52064201+tanpattaya@users.noreply.github.com> Date: Tue, 17 Dec 2019 15:54:48 +0700 Subject: [PATCH 022/213] Update index.html --- .../index.html" | 75 +++++++++++++------ 1 file changed, 53 insertions(+), 22 deletions(-) diff --git "a/\340\271\201\340\270\255\340\270\233 liff/index.html" "b/\340\271\201\340\270\255\340\270\233 liff/index.html" index 186175a..c08da83 100644 --- "a/\340\271\201\340\270\255\340\270\233 liff/index.html" +++ "b/\340\271\201\340\270\255\340\270\233 liff/index.html" @@ -1,24 +1,55 @@ - - - ꧁💓 @:꓄ꍏꈤᖘꍏ꓄꓄ꍏꌩ💓꧂BOTS LIFF-APP - - + - -
-
- HELLO !! - -
-

-

-

-

-

-
-

꧁💓 @:꓄ꍏꈤᖘꍏ꓄꓄ꍏꌩ💓꧂BOTS LIFF-APP - + + + - - - + + + + + + +

LINE Things Starter

+ <ชั่วโมง /> +

+ + อุปกรณ์ถูกตัดการเชื่อมต่อ + + <ชั่วโมง /> +