Add LinkedIn Jobs Enhanced script
This commit is contained in:
parent
8df5d36207
commit
d028eab15a
225
linkedin-jobs-enhanced/LinkedInJobsEnhanced.user.js
Normal file
225
linkedin-jobs-enhanced/LinkedInJobsEnhanced.user.js
Normal file
@ -0,0 +1,225 @@
|
||||
// ==UserScript==
|
||||
// @name LinkedIn Jobs Enhanced ✨
|
||||
// @namespace http://tampermonkey.net/
|
||||
// @version 2024-06-24T04:08:56+05:30
|
||||
// @description Trying to fix job filters without being paid by LinkedIn :')
|
||||
// @updateURL https://git.sangeeth.dev/x/userscripts/raw/branch/main/linkedin-jobs-enhanced/LinkedInJobsEnhanced.user.js
|
||||
// @downloadURL https://git.sangeeth.dev/x/userscripts/raw/branch/main/linkedin-jobs-enhanced/LinkedInJobsEnhanced.user.js
|
||||
// @author Sangeeth Sudheer
|
||||
// @match https://www.linkedin.com/jobs/search*
|
||||
// @match https://www.linkedin.com/jobs/collections*
|
||||
// @icon https://www.google.com/s2/favicons?sz=64&domain=linkedin.com
|
||||
// @grant GM_getValue
|
||||
// @grant GM_setValue
|
||||
// ==/UserScript==
|
||||
|
||||
/**
|
||||
** UTILITIES
|
||||
**/
|
||||
|
||||
function stringEnum(values) {
|
||||
return new Proxy(
|
||||
new Set(values),
|
||||
{
|
||||
get(target, prop) {
|
||||
if (!target.has(prop)) {
|
||||
throw Error(`${prop} is not a valid key!`);
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const Logger = {
|
||||
prefix: "linkedin-jobs-enhanced",
|
||||
|
||||
debug(...args) {
|
||||
if (Storage.get(Storage.keys.debug)) {
|
||||
console.log(`[${this.prefix}]`, ...args);
|
||||
}
|
||||
},
|
||||
|
||||
error(...args) {
|
||||
console.error(`[${this.prefix}]`, ...args);
|
||||
}
|
||||
};
|
||||
|
||||
const Storage = {
|
||||
_allKeys: stringEnum(["disliked", "liked", "debug", "mode"]),
|
||||
|
||||
init() {
|
||||
if (!Array.isArray(this.get(this.keys.disliked))) {
|
||||
this.set(this.keys.disliked, []);
|
||||
}
|
||||
|
||||
if (!Array.isArray(this.get(this.keys.liked))) {
|
||||
this.set(this.keys.liked, []);
|
||||
}
|
||||
|
||||
if (!this.get(this.keys.mode)) {
|
||||
this.set(this.keys.mode, "hide-disliked");
|
||||
}
|
||||
|
||||
if (typeof this.get(this.keys.debug) !== 'boolean') {
|
||||
this.set(this.keys.debug, false);
|
||||
}
|
||||
|
||||
Logger.debug(`Initialized storage`);
|
||||
},
|
||||
|
||||
get keys() {
|
||||
return this._allKeys;
|
||||
},
|
||||
|
||||
get(key) {
|
||||
return GM_getValue(key);
|
||||
},
|
||||
|
||||
set(key, value) {
|
||||
return GM_setValue(key, value);
|
||||
}
|
||||
};
|
||||
|
||||
const UI = {
|
||||
alert(msg) {
|
||||
alert(msg);
|
||||
}
|
||||
}
|
||||
|
||||
;(function() {
|
||||
'use strict';
|
||||
|
||||
function buildListPatterns() {
|
||||
const mode = Storage.get(Storage.keys.mode);
|
||||
let disliked = Storage.get(Storage.keys.disliked);
|
||||
let liked = Storage.get(Storage.keys.liked);
|
||||
|
||||
const trimSpaces = value => value.trim();
|
||||
const isValid = value => value && value.trim();
|
||||
|
||||
disliked = disliked.filter(isValid).map(trimSpaces);
|
||||
liked = liked.filter(isValid).map(trimSpaces);
|
||||
|
||||
if (mode === "show-liked" && liked.length === 0) {
|
||||
UI.alert("No valid liked companies set, check tampermonkey Storage tab");
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
if (disliked.length === 0) {
|
||||
UI.alert("No valid companies set, check tampermonkey Storage tab");
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
if (mode === "hide-disliked") {
|
||||
return [
|
||||
new RegExp("(" + disliked.join("|") + ")", "i"),
|
||||
null
|
||||
];
|
||||
}
|
||||
|
||||
if (mode === "show-liked") {
|
||||
return [
|
||||
null,
|
||||
new RegExp("(" + liked.join("|") + ")", "i")
|
||||
];
|
||||
}
|
||||
|
||||
throw new Error(`Unknown mode: ${mode} passed, check tampermonkey Storage tab`);
|
||||
}
|
||||
|
||||
function applyJobListItemVisibility(node) {
|
||||
const [HIDE_DISLIKED_PATTERN, SHOW_LIKED_PATTERN] = buildListPatterns();
|
||||
const mode = Storage.get(Storage.keys.mode);
|
||||
const companyNameEl = node.querySelector(".artdeco-entity-lockup__subtitle");
|
||||
|
||||
if (!companyNameEl) {
|
||||
Logger.debug("Skipping node because company name element not found", node);
|
||||
return;
|
||||
}
|
||||
|
||||
const companyName = companyNameEl.textContent.trim();
|
||||
|
||||
const matches = mode === "hide-disliked"
|
||||
? HIDE_DISLIKED_PATTERN.exec(companyName)
|
||||
: SHOW_LIKED_PATTERN.exec(companyName);
|
||||
|
||||
if ((mode === "hide-disliked" && matches) || (mode === "show-liked" && !matches)) {
|
||||
node.style.display = 'none';
|
||||
|
||||
if (mode === "hide-disliked") {
|
||||
Logger.debug(`Hid node because found a hide-disliked match: "${matches[1]}"`, node);
|
||||
} else {
|
||||
Logger.debug(`Hid node because ${companyName} is not one of the liked companies`);
|
||||
}
|
||||
} else if (node.style.display === 'none') {
|
||||
delete node.style.display;
|
||||
|
||||
Logger.debug("Node didn't match but was hidden already", node);
|
||||
}
|
||||
}
|
||||
|
||||
function isJobListItem(node) {
|
||||
return node.nodeType === Node.ELEMENT_NODE &&
|
||||
node.classList.contains("jobs-search-results__list-item");
|
||||
}
|
||||
|
||||
function handleMutations(records) {
|
||||
// TODO: Super crude, probably can be optimized but works fine-ish
|
||||
|
||||
let hasChange = false;
|
||||
|
||||
outer: for (const record of records) {
|
||||
for (const node of record.addedNodes) {
|
||||
if (isJobListItem(node)) {
|
||||
hasChange = true;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
|
||||
if (isJobListItem(record.target)) {
|
||||
hasChange = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasChange) {
|
||||
Logger.debug("No relevant mutations found, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
const jobListItems = document.querySelectorAll(".jobs-search-results__list-item");
|
||||
|
||||
for (const item of jobListItems) {
|
||||
applyJobListItemVisibility(item);
|
||||
}
|
||||
}
|
||||
|
||||
function areWeOk() {
|
||||
const mode = Storage.get(Storage.keys.mode);
|
||||
|
||||
const [HIDE_DISLIKED_PATTERN, SHOW_LIKED_PATTERN] = buildListPatterns();
|
||||
|
||||
if ((mode === "hide-disliked" && !HIDE_DISLIKED_PATTERN) || (mode === "show-liked" && !SHOW_LIKED_PATTERN)) {
|
||||
Logger.debug("Exiting since no patterns set");
|
||||
return;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Storage.init();
|
||||
|
||||
const mode = Storage.get(Storage.keys.mode);
|
||||
|
||||
Logger.debug(`Current mode is ${mode}`);
|
||||
|
||||
if (!areWeOk()) {
|
||||
Logger.debug("Not okay! Quitting");
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new MutationObserver(handleMutations);
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
})();
|
Loading…
Reference in New Issue
Block a user