-
Notifications
You must be signed in to change notification settings - Fork 0
/
syncViews.js
182 lines (160 loc) · 5.67 KB
/
syncViews.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
console.log("running syncViews.js");
// call synchronously while syncing views
createStyleElement();
syncViewStates();
listenForViewUpdates();
const viewList = document.querySelectorAll('[data-test-id="views_views-list_general-views-container"] > a > [data-test-id="views_views-list_row_title"]');
if (viewList.length >= 12) {
init();
}
else {
observeViewUpdates(); // TODO: also run when view refresh button is pressed - must wait for new views to load
}
async function init() {
await syncViews(); // call asynchronously
chrome.runtime.sendMessage({ synced: true });
console.log("synced");
}
function observeViewUpdates() {
console.log("observing view updates");
var observer = new MutationObserver(() => {
var viewList = document.querySelectorAll('[data-test-id="views_views-list_general-views-container"] > a > [data-test-id="views_views-list_row_title"]');
if (viewList.length >= 12) {
console.log(`found ${viewList.length} views`);
observer.disconnect();
init();
}
});
observer.observe(document, {
childList: true,
subtree: true,
});
}
async function syncViews() {
console.log("syncing views");
var internalId = 1;
const views = await getViews();
// hide view in UI if not currently shown in Zendesk
Object.values(views).forEach(view => {
const id = view.id;
if (!document.querySelector(`[data-view-id="${id}"]`)) {
console.log(`hiding view: ${id} from extension UI`);
views[id].active = false;
}
});
document.querySelectorAll('[data-test-id="views_views-list_general-views-container"] > a').forEach(view => {
if (view.style.display == "none") return; // skip views that are already not displayed by Zendesk
const id = view.getAttribute("data-view-id");
const title = view.firstElementChild.innerText;
const viewIds = new Set(Object.keys(views));
// update title and sort order if view is already in local storage
if (viewIds.has(id)) {
console.log(`view: ${id} already exists in local storage`);
views[id].internalId = internalId;
views[id].title = title;
views[id].active = true;
}
// add view if it is not already in local storage
else {
console.log(`adding view: ${id} to local storage`);
views[id] = {
id: id,
internalId: internalId, // used to sort views in extension UI in same order as in Zendesk
title: title,
displayed: true, // is the view currently toggled in the extension UI to be displayed
active: true // is the view currently shown in Zendesk
};
}
internalId++;
});
await setViews(views);
await syncViewStates(); // in case any views that were previously hidden but inactive became active again
}
function createStyleElement() {
if (document.getElementById("extension-styles")) {
console.log("style element already exists");
return;
}
console.log("creating style element");
const styleElement = document.createElement("style");
styleElement.id = "extension-styles";
document.querySelector("head").append(styleElement);
}
async function syncViewStates() {
console.log("syncing view states");
const styleElement = document.getElementById("extension-styles");
const views = await getViews();
const viewsWithRule = new Set();
const rules = styleElement.sheet.cssRules;
// remove rules from end of list first so indexes don't change
for (let i = rules.length - 1; i >= 0; i--) {
const rule = rules[i];
const id = rule.selectorText.match(/"([^"]+)"/)[1];
if (views.hasOwnProperty(id) && views[id].displayed) {
console.log(`syncViewState: removing rule for view: ${id}`);
styleElement.sheet.deleteRule(i);
}
else {
viewsWithRule.add(id);
}
}
Object.values(views).forEach(view => {
const id = view.id;
const displayed = view.displayed;
if (!displayed && !(viewsWithRule.has(id))) {
console.log(`syncViewState: adding rule for view: ${id}`);
styleElement.sheet.insertRule(`[data-view-id="${id}"] { display: none !important;}`);
}
});
}
function listenForViewUpdates() {
console.log(`listening for view updates`);
chrome.runtime.onMessage.addListener(msg => {
console.log(`received ${msg.action} message for view: ${msg.id}`);
const styleElement = document.getElementById("extension-styles");
const rules = styleElement.sheet.cssRules;
const ruleMapping = {};
for (let i = 0; i < rules.length; i++) {
const rule = rules[i];
const id = rule.selectorText.match(/"([^"]+)"/)[1];
ruleMapping[id] = i;
}
const id = msg.id;
const action = msg.action;
if (action == "show") {
if (!ruleMapping.hasOwnProperty(id)) {
console.log(`no rule to delete for view: ${id}`);
return;
}
console.log(`listenForViewUpdates: removing rule for view: ${id}`);
styleElement.sheet.deleteRule(ruleMapping[id]);
} else if (action == "hide") {
if (ruleMapping.hasOwnProperty(id)) {
console.log(`rule already exists for view: ${id}`);
return;
}
console.log(`listenForViewUpdates: adding rule for view: ${id}`);
styleElement.sheet.insertRule(`[data-view-id="${id}"] { display: none !important;}`);
}
});
}
function getViews() {
return new Promise((resolve, reject) => {
chrome.storage.local.get(["views"], value => {
if (chrome.runtime.lastError) {
return reject(chrome.runtime.lastError);
}
resolve(value.views);
});
});
}
function setViews(views) {
return new Promise((resolve, reject) => {
chrome.storage.local.set({ views: views }, () => {
if (chrome.runtime.lastError) {
return reject(chrome.runtime.lastError);
}
resolve();
});
});
}