-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
72 additions
and
98 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,107 +1,81 @@ | ||
<div id="graph"></div> | ||
<script> | ||
document.addEventListener('DOMContentLoaded', async function() { | ||
const apiBaseUrl = '{{ api_base_url }}'; | ||
const accessToken = sessionStorage.getItem('accessToken'); | ||
const userId = '{{ user.id }}'; | ||
const user = { id: userId, username: '{{ user.username }}', avatar: '{{ user.avatar }}' }; | ||
|
||
document.addEventListener('DOMContentLoaded', function() { | ||
const apiBaseUrl = '{{ api_base_url }}'; | ||
const accessToken = sessionStorage.getItem('accessToken'); // Ensure this is stored securely | ||
const userId = '{{ user.id }}'; | ||
const followers = await fetchAllItems(userId, 'followers'); | ||
const followings = await fetchAllItems(userId, 'following'); | ||
const graphData = generateGraphData(user, followers, followings); | ||
initGraph(graphData); | ||
}); | ||
|
||
Promise.all([ | ||
fetchAllItems(userId, 'followers'), | ||
fetchAllItems(userId, 'following') | ||
]).then(([followers, followings]) => { | ||
const user = { id: userId, username: 'User' }; // Mock user data, replace with actual data as needed | ||
const graphData = generateGraphData(user, followers, followings); | ||
initGraph(graphData); | ||
}); | ||
}); | ||
async function fetchAllItems(userId, endpoint) { | ||
let items = []; | ||
let maxId = null; | ||
do { | ||
const response = await fetch(`${apiBaseUrl}/${endpoint}/${userId}?limit=500&max_id=${maxId}`, { | ||
headers: { 'Authorization': `Bearer ${accessToken}` } | ||
}); | ||
const data = await response.json(); | ||
items = items.concat(data); | ||
maxId = data.length > 0 ? data[data.length - 1].id : null; | ||
} while (maxId != null); | ||
return items; | ||
} | ||
|
||
async function fetchAllItems(userId, endpoint) { | ||
let items = []; | ||
let maxId = null; | ||
do { | ||
const response = await fetch(`${apiBaseUrl}/${endpoint}/${userId}?limit=500&max_id=${maxId}`, { | ||
headers: { 'Authorization': `Bearer ${accessToken}` } | ||
}); | ||
const data = await response.json(); | ||
items = items.concat(data); | ||
maxId = data.length > 0 ? data[data.length - 1].id : null; | ||
} while (maxId != null); | ||
return items; | ||
} | ||
function generateGraphData(user, followers, followings) { | ||
const nodes = [{ id: user.id, username: user.username, type: 'user', avatar: user.avatar }]; | ||
const followerNodes = followers.map(f => ({ id: f.id, username: f.username, type: 'follower', avatar: f.avatar })); | ||
const followingNodes = followings.map(f => ({ id: f.id, username: f.username, type: 'following', avatar: f.avatar })); | ||
nodes.push(...followerNodes, ...followingNodes); | ||
const links = followerNodes.map(f => ({ source: user.id, target: f.id })) | ||
.concat(followingNodes.map(f => ({ source: user.id, target: f.id }))); | ||
return { nodes, links }; | ||
} | ||
|
||
function generateGraphData(user, followers, followings) { | ||
const nodes = [{ id: user.id, username: user.username, type: 'user' }]; | ||
const followerNodes = followers.map(f => ({ id: f.id, username: f.username, type: 'follower' })); | ||
const followingNodes = followings.map(f => ({ id: f.id, username: f.username, type: 'following' })); | ||
nodes.push(...followerNodes, ...followingNodes); | ||
const links = followerNodes.map(f => ({ source: user.id, target: f.id })) | ||
.concat(followingNodes.map(f => ({ source: user.id, target: f.id }))); | ||
return { nodes, links }; | ||
} | ||
function initGraph(graphData) { | ||
const Graph = ForceGraph()(document.getElementById('graph')) | ||
.backgroundColor('#F5F5FF') | ||
.height(window.innerHeight - 60) | ||
.graphData(graphData) | ||
.nodeCanvasObject((node, ctx, globalScale) => { | ||
const label = node.username; | ||
const fontSize = 12 / globalScale; | ||
const imgSize = 28; | ||
ctx.font = `${fontSize}px Sans-Serif`; | ||
ctx.textAlign = 'center'; | ||
ctx.textBaseline = 'middle'; | ||
if (node.avatar && node.img) { | ||
ctx.drawImage(node.img, node.x - imgSize / 2, node.y - imgSize / 2, imgSize, imgSize); | ||
} else if (node.avatar && !node.img) { | ||
const img = new Image(); | ||
img.src = node.avatar; | ||
img.onload = () => { | ||
node.img = img; | ||
ctx.drawImage(img, node.x - imgSize / 2, node.y - imgSize / 2, imgSize, imgSize); | ||
}; | ||
} | ||
ctx.fillStyle = (node.type === 'follower') ? 'red' : 'blue'; | ||
ctx.fillText(label, node.x, node.y + imgSize / 2 + 5); | ||
}) | ||
.onNodeClick(node => console.log(node)); | ||
|
||
function initGraph(graphData) { | ||
const Graph = ForceGraph()(document.getElementById('graph')) | ||
.backgroundColor('#F5F5FF') | ||
.height(window.innerHeight - 60) | ||
.graphData(graphData) | ||
.nodeCanvasObject((node, ctx, globalScale) => { | ||
const label = node.username; | ||
const fontSize = 12 / globalScale; | ||
const imgSize = 28; // Adjust size as needed | ||
ctx.font = `${fontSize}px Sans-Serif`; | ||
ctx.textAlign = 'center'; | ||
ctx.textBaseline = 'middle'; | ||
Graph.d3Force('charge', d3.forceManyBody().strength(-120)); | ||
Graph.d3Force('link', d3.forceLink().distance(140)); | ||
} | ||
|
||
// Draw avatar images | ||
if (node.avatar && node.img) { | ||
ctx.drawImage(node.img, node.x - imgSize / 2, node.y - imgSize / 2, imgSize, imgSize); | ||
} else if (node.avatar && !node.img) { | ||
const img = new Image(); | ||
img.src = node.avatar; | ||
img.onload = () => { | ||
node.img = img; | ||
ctx.drawImage(img, node.x - imgSize / 2, node.y - imgSize / 2, imgSize, imgSize); | ||
}; | ||
} | ||
|
||
// Draw labels | ||
ctx.fillStyle = (node.type === 'follower') ? 'red' : 'blue'; | ||
ctx.fillText(label, node.x, node.y + imgSize / 2 + 5); | ||
}) | ||
.onNodeClick(node => console.log(node)); | ||
|
||
// Configuration for forces | ||
Graph.d3Force('charge', d3.forceManyBody().strength(-120)); | ||
Graph.d3Force('link', d3.forceLink().distance(140)); | ||
|
||
elementResizeDetectorMaker().listenTo( | ||
document.getElementById('graph'), | ||
el => Graph.width(el.offsetWidth) | ||
); | ||
} | ||
|
||
// Configuration for forces | ||
Graph.d3Force('charge', d3.forceManyBody().strength(-120)); | ||
Graph.d3Force('link', d3.forceLink().distance(140)); | ||
|
||
elementResizeDetectorMaker().listenTo( | ||
document.getElementById('graph'), | ||
el => Graph.width(el.offsetWidth) | ||
); | ||
|
||
function filterNodes(filterType) { | ||
const filteredData = { | ||
nodes: graphData.nodes.filter(node => filterType === 'all' || node.type === filterType), | ||
links: graphData.links.filter(link => { | ||
const sourceVisible = filterType === 'all' || link.source.type === filterType; | ||
const targetVisible = filterType === 'all' || link.target.type === filterType; | ||
return sourceVisible && targetVisible; | ||
}) | ||
}; | ||
|
||
// Update the graph with the filtered data | ||
Graph.graphData(filteredData); | ||
} | ||
|
||
function filterNodes(filterType) { | ||
const filteredData = { | ||
nodes: graphData.nodes.filter(node => filterType === 'all' || node.type === filterType), | ||
links: graphData.links.filter(link => { | ||
const sourceVisible = filterType === 'all' || link.source.type === filterType; | ||
const targetVisible = filterType === 'all' || link.target.type === filterType; | ||
return sourceVisible && targetVisible; | ||
}) | ||
}; | ||
Graph.graphData(filteredData); // Assuming 'Graph' is your ForceGraph instance | ||
} | ||
</script> |