Skip to content

Commit

Permalink
fix: 백준 익스텐션 재로그인 권유 (#121)
Browse files Browse the repository at this point in the history
* fix: 백준익스텐션 재로그인 권유

* fix: 백준 배포 관련 수정
  • Loading branch information
jinoov authored Jul 23, 2024
1 parent 3054a4f commit 7fb7ae0
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 227 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ yarn-error.log*

node_modules

# chrome extension zip files
baekjoon-hub.zip
2 changes: 1 addition & 1 deletion apps/baekjoon-hub/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "PoolC Baekjoon Hub",
"version": "1.0.3",
"version": "1.0.4",
"description": "Yonsei Univ Programming Club PoolC integration with Baekjoon",
"action": {
"default_icon": "assets/images/logo-poolc.png",
Expand Down
8 changes: 7 additions & 1 deletion apps/baekjoon-hub/scripts/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ const POOLC_BASE_URL = 'https://poolc.org/api';

const appFetch = (...args) =>
fetch(...args).then((res) => {
if (res.status === 401) {
throw new Error('unauthorized');
}

if (res.status >= 400) {
throw new Error('fetch error. please check network tab');
}
Expand Down Expand Up @@ -29,7 +33,9 @@ function handleMessage(request, _, sendResponse) {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
}).then(() => sendResponse({ success: true }));
})
.then(() => sendResponse({ success: true }))
.catch(() => sendResponse({ success: false }));
});
}
// login check
Expand Down
52 changes: 44 additions & 8 deletions apps/baekjoon-hub/scripts/baekjoon/baekjoon.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,33 @@ function showToast(message) {
}, 2000);
}

function showErrorToast(message) {
const toast = `<div>${message}</div>`;
const box = document.createElement('div');
box.innerHTML = toast;
box.style.position = 'fixed';
box.style.top = '20px';
box.style.right = '20px';
box.style.backgroundColor = '#e03131';
box.style.boxShadow = '0 2px 7px 0 rgba(0, 0, 0, 0.2)';
box.style.padding = '20px';
box.style.zIndex = 9999;
box.style.transition = 'opacity .3s linear';
box.style.color = '#fff';
box.style.fontWeight = 700;

document.body.append(box);

console.log('toast!');
setTimeout(() => {
box.style.opacity = 0;
}, 2000 - 300);

setTimeout(() => {
box.remove();
}, 2000);
}

function stopLoader() {
clearInterval(loader);
loader = null;
Expand Down Expand Up @@ -85,14 +112,23 @@ function startLoader() {

const psData = await findData();

await notifyProblemSolved({
language: psData.language,
level: psData.level,
problemId: psData.problemId,
problemTags: psData.tags.map((tag) => tag.key), // 국문 or 영문 어떤게 더 낫지??
submissionId: psData.submissionId,
title: psData.titles[0].title,
});
try {
await notifyProblemSolved({
language: psData.language,
level: psData.level,
problemId: psData.problemId,
problemTags: psData.tags.map((tag) => tag.key), // 국문 or 영문 어떤게 더 낫지??
submissionId: psData.submissionId,
title: psData.titles[0].title,
});
} catch (error) {
showErrorToast(`
인증이 만료되었습니다. 😭<br/>
<a href=${`chrome-extension://${chrome.runtime.id}/login.html`}>해당 링크</a>를 통해 다시 로그인해주세요. 🙏
`);
console.error(error);
return;
}

await reIssueToken();
showToast(`${psData.problemId}번 문제 풀이가 풀씨에 업로드되었습니다. ❤`);
Expand Down
10 changes: 8 additions & 2 deletions apps/baekjoon-hub/scripts/baekjoon/parsing.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ async function SolvedApiCall(problemId) {
}

async function notifyProblemSolved({ language, level, problemId, problemTags, submissionId, title }) {
const response = await new Promise((resolve) => {
const response = await new Promise((resolve, reject) => {
chrome.runtime.sendMessage(
{
poolc: {
Expand All @@ -138,7 +138,13 @@ async function notifyProblemSolved({ language, level, problemId, problemTags, su
},
},
},
resolve,
(res) => {
if (res.success) {
resolve();
} else {
reject();
}
},
);
});
return response;
Expand Down
215 changes: 0 additions & 215 deletions apps/baekjoon-hub/scripts/util.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,3 @@
/**
*현재 익스텐션의 버전정보를 반환합니다.
* @returns {string} - 현재 익스텐션의 버전정보
*/
function getVersion() {
return chrome.runtime.getManifest().version;
}

/** element가 존재하는지 반환합니다.
* @param {DOMElement} element - 존재하는지 확인할 element
* @returns {boolean} - 존재하면 true, 존재하지 않으면 false
*/
function elementExists(element) {
return element !== undefined && element !== null && element.hasOwnProperty('length') && element.length > 0;
}

/**
* 해당 값이 null 또는 undefined인지 체크합니다.
* @param {any} value - 체크할 값
* @returns {boolean} - null이면 true, null이 아니면 false
*/
function isNull(value) {
return value === null || value === undefined;
}

/**
* 해당 값이 비어있거나 빈 문자열인지 체크합니다.
* @param {any} value - 체크할 값
* @returns {boolean} - 비어있으면 true, 비어있지 않으면 false
*/
function isEmpty(value) {
return isNull(value) || (value.hasOwnProperty('length') && value.length === 0);
}

/** 객체 또는 배열의 모든 요소를 재귀적으로 순회하여 값이 비어있지 않은지 체크합니다.
* 자기 자신의 null값이거나 빈 문자열, 빈 배열, 빈 객체인 경우이거나, 요소 중 하나라도 값이 비어있으면 false를 반환합니다.
* @param {any} obj - 체크할 객체 또는 배열
* @returns {boolean} - 비어있지 않으면 true, 비어있으면 false
*/
function isNotEmpty(obj) {
if (isEmpty(obj)) return false;
if (typeof obj !== 'object') return true;
if (obj.length === 0) return false;
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
if (!isNotEmpty(obj[key])) return false;
}
}
return true;
}
/**
* 문자열을 escape 하여 반환합니다.
* @param {string} text - escape 할 문자열
Expand Down Expand Up @@ -102,171 +52,6 @@ String.prototype.unescapeHtml = function () {
return unescapeHtml(this);
};

/** 일반 특수문자를 전각문자로 변환하는 함수
* @param {string} text - 변환할 문자열
* @returns {string} - 전각문자로 변환된 문자열
*/
function convertSingleCharToDoubleChar(text) {
// singleChar to doubleChar mapping
const map = {
'!': '!',
'%': '%',
'&': '&',
'(': '(',
')': ')',
'*': '*',
'+': '+',
',': ',',
'-': '-',
'.': '.',
'/': '/',
':': ':',
';': ';',
'<': '<',
'=': '=',
'>': '>',
'?': '?',
'@': '@',
'[': '[',
'\\': '\',
']': ']',
'^': '^',
_: '_',
'`': '`',
'{': '{',
'|': '|',
'}': '}',
'~': '~',
' ': ' ', // 공백만 전각문자가 아닌 FOUR-PER-EM SPACE로 변환
};
return text.replace(/[!%&()*+,\-./:;<=>?@\[\\\]^_`{|}~ ]/g, function (m) {
return map[m];
});
}

/**
* base64로 문자열을 base64로 인코딩하여 반환합니다.
* @param {string} str - base64로 인코딩할 문자열
* @returns {string} - base64로 인코딩된 문자열
*/
function b64EncodeUnicode(str) {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(`0x${p1}`);
}),
);
}

/**
* base64로 인코딩된 문자열을 base64로 디코딩하여 반환합니다.
* @param {string} b64str - base64로 인코딩된 문자열
* @returns {string} - base64로 디코딩된 문자열
*/
function b64DecodeUnicode(b64str) {
return decodeURIComponent(
atob(b64str)
.split('')
.map(function (c) {
return `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`;
})
.join(''),
);
}

function parseNumberFromString(str) {
const numbers = str.match(/\d+/g);
if (isNotEmpty(numbers) && numbers.length > 0) {
return Number(numbers[0]);
}
return NaN;
}

/** key 값을 기준으로 array를 그룹핑하여 map으로 반환합니다.
* @param {object} array - array to be sorted
* @param {string} key - key to sort
* @returns {object} - key 기준으로 그룹핑된 객체들 배열을 value로 갖는 map
*/
function groupBy(array, key) {
return array.reduce(function (rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
}

/**
* arr에서 같은 key 그룹 내의 요소 중 최고의 값을 리스트화하여 반환합니다.
* @param arr: 비교할 요소가 있는 배열
* @param key: 같은 그룹으로 묶을 키 값
* @param compare: 비교할 함수
* @returns {array<object>} : 같은 key 그룹 내의 요소 중 최고의 값을 반환합니다.
* */
function maxValuesGroupBykey(arr, key, compare) {
const map = groupBy(arr, key);
const result = [];
for (const [key, value] of Object.entries(map)) {
const maxValue = value.reduce((max, current) => {
return compare(max, current) > 0 ? max : current;
});
result.push(maxValue);
}
return result;
}

/** 배열 내의 key에 val 값을 포함하고 있는 요소만을 반환합니다.
* @param {array} arr - array to be filtered
* @param {object} conditions - object of key, values to be used in filter
* @returns {array} - filtered array
*/
function filter(arr, conditions) {
return arr.filter((item) => {
for (const [key, value] of Object.entries(conditions)) if (!item[key].includes(value)) return false;
return true;
});
}

/** calculate github blob file SHA
* @param {string} content - file content
* @returns {string} - SHA hash
*/
function calculateBlobSHA(content) {
return sha1(`blob ${new Blob([content]).size}\0${content}`);
}

/**
* asyncPool https://github.com/rxaviers/async-pool/blob/master/lib/es7.js
* @param {number} poolLimit - pool limit
* @param {array} array - array to be processed
* @param {function} iteratorFn - iterator function
* @returns {array} - processed array
*/
async function asyncPool(poolLimit, array, iteratorFn) {
const ret = [];
const executing = [];
for (const item of array) {
const p = Promise.resolve().then(() => iteratorFn(item, array));
ret.push(p);

if (poolLimit <= array.length) {
const e = p.then(() => executing.splice(executing.indexOf(e), 1));
executing.push(e);
if (executing.length >= poolLimit) {
await Promise.race(executing);
}
}
}
return Promise.all(ret);
}

/**
* combine two array<Object> same index.
* @param {array<Object>} a
* @param {array<Object>} b
* @return {array<Object>}
*/
function combine(a, b) {
return a.map((x, i) => ({ ...x, ...b[i] }));
}

if (typeof __DEV__ !== 'undefined') {
const exports = (module.exports = {});
exports.filter = filter;
Expand Down

0 comments on commit 7fb7ae0

Please sign in to comment.