feat(webui): add Mods card (#252)
This commit is contained in:
		
							parent
							
								
									ef220ca6d7
								
							
						
					
					
						commit
						f83f80c991
					
				@ -1,16 +1,19 @@
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
import { MinItem, warframes, weapons, items } from "@/src/services/itemDataService";
 | 
			
		||||
import badItems from "@/static/json/exclude-mods.json";
 | 
			
		||||
 | 
			
		||||
interface ListedItem {
 | 
			
		||||
    uniqueName: string;
 | 
			
		||||
    name: string;
 | 
			
		||||
    fusionLimit?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function reduceItems(items: MinItem[]): ListedItem[] {
 | 
			
		||||
    return items.map((item: MinItem): ListedItem => {
 | 
			
		||||
        return {
 | 
			
		||||
            uniqueName: item.uniqueName,
 | 
			
		||||
            name: item.name
 | 
			
		||||
            name: item.name,
 | 
			
		||||
            fusionLimit: (item as any).fusionLimit
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
@ -19,7 +22,11 @@ const getItemListsController: RequestHandler = (_req, res) => {
 | 
			
		||||
    res.json({
 | 
			
		||||
        warframes: reduceItems(warframes),
 | 
			
		||||
        weapons: reduceItems(weapons.filter(item => item.productCategory != "OperatorAmps")),
 | 
			
		||||
        miscitems: reduceItems(items.filter(item => item.category == "Misc" || item.category == "Resources"))
 | 
			
		||||
        miscitems: reduceItems(
 | 
			
		||||
            items.filter(item => item.category == "Misc" || item.category == "Resources" || item.category == "Fish")
 | 
			
		||||
        ),
 | 
			
		||||
        mods: reduceItems(items.filter(item => item.category == "Mods" || item.category == "Arcanes")),
 | 
			
		||||
        badItems
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -45,16 +45,32 @@
 | 
			
		||||
                    ></button>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="offcanvas-body">
 | 
			
		||||
                    <ul>
 | 
			
		||||
                        <li>
 | 
			
		||||
                            <a href="/webui/inventory" data-bs-dismiss="offcanvas" data-bs-target="#sidebar"
 | 
			
		||||
                                >Inventory</a
 | 
			
		||||
                    <div class="navbar p-0">
 | 
			
		||||
                        <ul class="navbar-nav justify-content-end">
 | 
			
		||||
                            <li class="nav-item">
 | 
			
		||||
                                <a
 | 
			
		||||
                                    class="nav-link"
 | 
			
		||||
                                    href="/webui/inventory"
 | 
			
		||||
                                    data-bs-dismiss="offcanvas"
 | 
			
		||||
                                    data-bs-target="#sidebar"
 | 
			
		||||
                                >
 | 
			
		||||
                                    Inventory
 | 
			
		||||
                                </a>
 | 
			
		||||
                            </li>
 | 
			
		||||
                            <li class="nav-item">
 | 
			
		||||
                                <a
 | 
			
		||||
                                    class="nav-link"
 | 
			
		||||
                                    href="/webui/mods"
 | 
			
		||||
                                    data-bs-dismiss="offcanvas"
 | 
			
		||||
                                    data-bs-target="#sidebar"
 | 
			
		||||
                                >
 | 
			
		||||
                                    Mods
 | 
			
		||||
                                </a>
 | 
			
		||||
                            </li>
 | 
			
		||||
                        <li><a href="/webui/mods" data-bs-dismiss="offcanvas" data-bs-target="#sidebar">Mods</a></li>
 | 
			
		||||
                        </ul>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
                <p id="refresh-note" class="mb-4">
 | 
			
		||||
                    Note: Changes made here will only be reflected in-game when the game re-downloads your inventory.
 | 
			
		||||
@ -117,17 +133,27 @@
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div data-route="/webui/mods" data-title="Mods | OpenWF WebUI">
 | 
			
		||||
                    <div class="row">
 | 
			
		||||
                        <div class="col-xxl-6">
 | 
			
		||||
                            <div class="card mb-4">
 | 
			
		||||
                                <h5 class="card-header">Add Riven</h5>
 | 
			
		||||
                                <form class="card-body" onsubmit="doAcquireRiven();return false;">
 | 
			
		||||
                                    <select class="form-control mb-3" id="addriven-type">
 | 
			
		||||
                                        <option value="LotusArchgunRandomModRare">LotusArchgunRandomModRare</option>
 | 
			
		||||
                                <option value="LotusModularMeleeRandomModRare">LotusModularMeleeRandomModRare</option>
 | 
			
		||||
                                <option value="LotusModularPistolRandomModRare">LotusModularPistolRandomModRare</option>
 | 
			
		||||
                                        <option value="LotusModularMeleeRandomModRare">
 | 
			
		||||
                                            LotusModularMeleeRandomModRare
 | 
			
		||||
                                        </option>
 | 
			
		||||
                                        <option value="LotusModularPistolRandomModRare">
 | 
			
		||||
                                            LotusModularPistolRandomModRare
 | 
			
		||||
                                        </option>
 | 
			
		||||
                                        <option value="LotusPistolRandomModRare">LotusPistolRandomModRare</option>
 | 
			
		||||
                                <option value="LotusRifleRandomModRare" selected>LotusRifleRandomModRare</option>
 | 
			
		||||
                                        <option value="LotusRifleRandomModRare" selected>
 | 
			
		||||
                                            LotusRifleRandomModRare
 | 
			
		||||
                                        </option>
 | 
			
		||||
                                        <option value="LotusShotgunRandomModRare">LotusShotgunRandomModRare</option>
 | 
			
		||||
                                <option value="PlayerMeleeWeaponRandomModRare">PlayerMeleeWeaponRandomModRare</option>
 | 
			
		||||
                                        <option value="PlayerMeleeWeaponRandomModRare">
 | 
			
		||||
                                            PlayerMeleeWeaponRandomModRare
 | 
			
		||||
                                        </option>
 | 
			
		||||
                                    </select>
 | 
			
		||||
                                    <textarea
 | 
			
		||||
                                        id="addriven-fingerprint"
 | 
			
		||||
@ -135,10 +161,12 @@
 | 
			
		||||
                                        placeholder="Fingerprint"
 | 
			
		||||
                                    ></textarea>
 | 
			
		||||
                                    <button class="btn btn-primary" style="margin-right: 5px" type="submit">Add</button>
 | 
			
		||||
                            <a href="https://riven.builds.wf/" target="_blank">Need help with the fingerprint?</a>
 | 
			
		||||
                                    <a href="https://riven.builds.wf/" target="_blank">
 | 
			
		||||
                                        Need help with the fingerprint?
 | 
			
		||||
                                    </a>
 | 
			
		||||
                                </form>
 | 
			
		||||
                            </div>
 | 
			
		||||
                    <div class="card mb-4 col-xxl-6">
 | 
			
		||||
                            <div class="card mb-4">
 | 
			
		||||
                                <h5 class="card-header">Rivens</h5>
 | 
			
		||||
                                <div class="card-body">
 | 
			
		||||
                                    <table class="table table-hover w-100">
 | 
			
		||||
@ -147,11 +175,28 @@
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="col-xxl-6">
 | 
			
		||||
                            <div class="card mb-4">
 | 
			
		||||
                                <h5 class="card-header">Mods</h5>
 | 
			
		||||
                                <div class="card-body">
 | 
			
		||||
                                    <table class="table table-hover w-100">
 | 
			
		||||
                                        <tbody id="mods-list"></tbody>
 | 
			
		||||
                                    </table>
 | 
			
		||||
                                    <form class="input-group" onsubmit="doAcquireMod();return false;">
 | 
			
		||||
                                        <input class="form-control" id="mod-to-acquire" list="datalist-mods" />
 | 
			
		||||
                                        <button class="btn btn-primary" type="submit">Add</button>
 | 
			
		||||
                                    </form>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <datalist id="datalist-warframes"></datalist>
 | 
			
		||||
        <datalist id="datalist-weapons"></datalist>
 | 
			
		||||
        <datalist id="datalist-miscitems"></datalist>
 | 
			
		||||
        <datalist id="datalist-mods"></datalist>
 | 
			
		||||
        <script src="libs/jquery-3.6.0.min.js"></script>
 | 
			
		||||
        <script src="libs/whirlpool-js.min.js"></script>
 | 
			
		||||
        <script src="libs/single.js"></script>
 | 
			
		||||
 | 
			
		||||
@ -76,33 +76,47 @@ single.on("route_load", function (event) {
 | 
			
		||||
    } else {
 | 
			
		||||
        $("body").removeClass("logged-in");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $(".nav-link").removeClass("active");
 | 
			
		||||
    const navLink = document.querySelector(".nav-link[href='" + event.route.paths[0] + "']");
 | 
			
		||||
    if (navLink) {
 | 
			
		||||
        navLink.classList.add("active");
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
window.itemListPromise = new Promise(resolve => {
 | 
			
		||||
    const req = $.get("/custom/getItemLists");
 | 
			
		||||
    req.done(data => {
 | 
			
		||||
        const itemMap = {};
 | 
			
		||||
        const itemMap = {
 | 
			
		||||
            // Generics for rivens
 | 
			
		||||
            "/Lotus/Weapons/Tenno/Archwing/Primary/ArchGun": { name: "Archgun" },
 | 
			
		||||
            "/Lotus/Weapons/Tenno/Melee/PlayerMeleeWeapon": { name: "Melee" },
 | 
			
		||||
            "/Lotus/Weapons/Tenno/Pistol/LotusPistol": { name: "Pistol" },
 | 
			
		||||
            "/Lotus/Weapons/Tenno/Rifle/LotusRifle": { name: "Rifle" },
 | 
			
		||||
            "/Lotus/Weapons/Tenno/Shotgun/LotusShotgun": { name: "Shotgun" },
 | 
			
		||||
            // Missing in data sources
 | 
			
		||||
            "/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod": { name: "Traumatic Peculiar" },
 | 
			
		||||
            "/Lotus/Weapons/Tenno/Grimoire/TnGrimoire": { name: "Grimoire" }
 | 
			
		||||
        };
 | 
			
		||||
        for (const [type, items] of Object.entries(data)) {
 | 
			
		||||
            if (type != "badItems") {
 | 
			
		||||
                items.forEach(item => {
 | 
			
		||||
                    if (item.uniqueName in data.badItems) {
 | 
			
		||||
                        item.name += " (Imposter)";
 | 
			
		||||
                    } else if (item.uniqueName.substr(0, 18) != "/Lotus/Types/Game/") {
 | 
			
		||||
                        const option = document.createElement("option");
 | 
			
		||||
                        option.setAttribute("data-key", item.uniqueName);
 | 
			
		||||
                        option.value = item.name;
 | 
			
		||||
                        document.getElementById("datalist-" + type).appendChild(option);
 | 
			
		||||
                    }
 | 
			
		||||
                    itemMap[item.uniqueName] = { ...item, type };
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        resolve(itemMap);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const rivenGenericCompatNames = {
 | 
			
		||||
    "/Lotus/Weapons/Tenno/Archwing/Primary/ArchGun": "Archgun",
 | 
			
		||||
    "/Lotus/Weapons/Tenno/Melee/PlayerMeleeWeapon": "Melee",
 | 
			
		||||
    "/Lotus/Weapons/Tenno/Pistol/LotusPistol": "Pistol",
 | 
			
		||||
    "/Lotus/Weapons/Tenno/Rifle/LotusRifle": "Rifle",
 | 
			
		||||
    "/Lotus/Weapons/Tenno/Shotgun/LotusShotgun": "Shotgun"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function updateInventory() {
 | 
			
		||||
    const req = $.get("/api/inventory.php?" + window.authz);
 | 
			
		||||
    req.done(data => {
 | 
			
		||||
@ -190,6 +204,7 @@ function updateInventory() {
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            document.getElementById("riven-list").innerHTML = "";
 | 
			
		||||
            document.getElementById("mods-list").innerHTML = "";
 | 
			
		||||
            data.Upgrades.forEach(item => {
 | 
			
		||||
                if (item.ItemType.substr(0, 32) == "/Lotus/Upgrades/Mods/Randomized/") {
 | 
			
		||||
                    const rivenType = item.ItemType.substr(32);
 | 
			
		||||
@ -198,10 +213,7 @@ function updateInventory() {
 | 
			
		||||
                    const tr = document.createElement("tr");
 | 
			
		||||
                    {
 | 
			
		||||
                        const td = document.createElement("td");
 | 
			
		||||
                        td.textContent =
 | 
			
		||||
                            itemMap[fingerprint.compat]?.name ??
 | 
			
		||||
                            rivenGenericCompatNames[fingerprint.compat] ??
 | 
			
		||||
                            fingerprint.compat;
 | 
			
		||||
                        td.textContent = itemMap[fingerprint.compat]?.name ?? fingerprint.compat;
 | 
			
		||||
                        td.textContent += " " + RivenParser.parseRiven(rivenType, fingerprint, 1).name;
 | 
			
		||||
                        td.innerHTML += " <span title='Number of buffs'>▲ " + fingerprint.buffs.length + "</span>";
 | 
			
		||||
                        td.innerHTML += " <span title='Number of curses'>▼ " + fingerprint.curses.length + "</span>";
 | 
			
		||||
@ -245,6 +257,92 @@ function updateInventory() {
 | 
			
		||||
                        tr.appendChild(td);
 | 
			
		||||
                    }
 | 
			
		||||
                    document.getElementById("riven-list").appendChild(tr);
 | 
			
		||||
                } else {
 | 
			
		||||
                    const tr = document.createElement("tr");
 | 
			
		||||
                    const rank = parseInt(JSON.parse(item.UpgradeFingerprint).lvl);
 | 
			
		||||
                    const maxRank = itemMap[item.ItemType]?.fusionLimit ?? 5;
 | 
			
		||||
                    {
 | 
			
		||||
                        const td = document.createElement("td");
 | 
			
		||||
                        td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
 | 
			
		||||
                        td.innerHTML += " <span title='Rank'>★ " + rank + "/" + maxRank + "</span>";
 | 
			
		||||
                        tr.appendChild(td);
 | 
			
		||||
                    }
 | 
			
		||||
                    {
 | 
			
		||||
                        const td = document.createElement("td");
 | 
			
		||||
                        td.classList = "text-end";
 | 
			
		||||
                        if (rank < maxRank) {
 | 
			
		||||
                            const a = document.createElement("a");
 | 
			
		||||
                            a.href = "#";
 | 
			
		||||
                            a.onclick = function (event) {
 | 
			
		||||
                                event.preventDefault();
 | 
			
		||||
                                setFingerprint(item.ItemType, item.ItemId, { lvl: maxRank });
 | 
			
		||||
                            };
 | 
			
		||||
                            a.textContent = "Max Rank";
 | 
			
		||||
                            td.appendChild(a);
 | 
			
		||||
 | 
			
		||||
                            const span = document.createElement("span");
 | 
			
		||||
                            span.innerHTML = " · ";
 | 
			
		||||
                            td.appendChild(span);
 | 
			
		||||
                        }
 | 
			
		||||
                        {
 | 
			
		||||
                            const a = document.createElement("a");
 | 
			
		||||
                            a.href = "#";
 | 
			
		||||
                            a.onclick = function (event) {
 | 
			
		||||
                                event.preventDefault();
 | 
			
		||||
                                disposeOfGear("Upgrades", item.ItemId.$oid);
 | 
			
		||||
                            };
 | 
			
		||||
                            a.textContent = "Remove";
 | 
			
		||||
                            td.appendChild(a);
 | 
			
		||||
                        }
 | 
			
		||||
                        tr.appendChild(td);
 | 
			
		||||
                    }
 | 
			
		||||
                    document.getElementById("mods-list").appendChild(tr);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            data.RawUpgrades.forEach(item => {
 | 
			
		||||
                if (item.ItemCount > 0) {
 | 
			
		||||
                    const maxRank = itemMap[item.ItemType]?.fusionLimit ?? 5;
 | 
			
		||||
                    const tr = document.createElement("tr");
 | 
			
		||||
                    {
 | 
			
		||||
                        const td = document.createElement("td");
 | 
			
		||||
                        td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
 | 
			
		||||
                        td.innerHTML += " <span title='Rank'>★ 0/" + maxRank + "</span>";
 | 
			
		||||
                        if (item.ItemCount > 1) {
 | 
			
		||||
                            td.innerHTML += " <span title='Count'>🗍 " + parseInt(item.ItemCount) + "</span>";
 | 
			
		||||
                        }
 | 
			
		||||
                        tr.appendChild(td);
 | 
			
		||||
                    }
 | 
			
		||||
                    {
 | 
			
		||||
                        const td = document.createElement("td");
 | 
			
		||||
                        td.classList = "text-end";
 | 
			
		||||
                        {
 | 
			
		||||
                            const a = document.createElement("a");
 | 
			
		||||
                            a.href = "#";
 | 
			
		||||
                            a.onclick = function (event) {
 | 
			
		||||
                                event.preventDefault();
 | 
			
		||||
                                setFingerprint(item.ItemType, item.LastAdded, { lvl: maxRank });
 | 
			
		||||
                            };
 | 
			
		||||
                            a.textContent = "Max Rank";
 | 
			
		||||
                            td.appendChild(a);
 | 
			
		||||
                        }
 | 
			
		||||
                        {
 | 
			
		||||
                            const span = document.createElement("span");
 | 
			
		||||
                            span.innerHTML = " · ";
 | 
			
		||||
                            td.appendChild(span);
 | 
			
		||||
                        }
 | 
			
		||||
                        {
 | 
			
		||||
                            const a = document.createElement("a");
 | 
			
		||||
                            a.href = "#";
 | 
			
		||||
                            a.onclick = function (event) {
 | 
			
		||||
                                event.preventDefault();
 | 
			
		||||
                                disposeOfItems("Upgrades", item.ItemType, item.ItemCount);
 | 
			
		||||
                            };
 | 
			
		||||
                            a.textContent = "Remove";
 | 
			
		||||
                            td.appendChild(a);
 | 
			
		||||
                        }
 | 
			
		||||
                        tr.appendChild(td);
 | 
			
		||||
                    }
 | 
			
		||||
                    document.getElementById("mods-list").appendChild(tr);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
@ -352,6 +450,29 @@ function disposeOfGear(category, oid) {
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function disposeOfItems(category, type, count) {
 | 
			
		||||
    const data = {
 | 
			
		||||
        SellCurrency: "SC_RegularCredits",
 | 
			
		||||
        SellPrice: 0,
 | 
			
		||||
        Items: {}
 | 
			
		||||
    };
 | 
			
		||||
    data.Items[category] = [
 | 
			
		||||
        {
 | 
			
		||||
            String: type,
 | 
			
		||||
            Count: count
 | 
			
		||||
        }
 | 
			
		||||
    ];
 | 
			
		||||
    revalidateAuthz(() => {
 | 
			
		||||
        $.post({
 | 
			
		||||
            url: "/api/sell.php?" + window.authz,
 | 
			
		||||
            contentType: "text/plain",
 | 
			
		||||
            data: JSON.stringify(data)
 | 
			
		||||
        }).done(function () {
 | 
			
		||||
            updateInventory();
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function doAcquireMiscItems() {
 | 
			
		||||
    const uniqueName = getKey(document.getElementById("miscitem-type"));
 | 
			
		||||
    if (!uniqueName) {
 | 
			
		||||
@ -445,3 +566,53 @@ function doAcquireRiven() {
 | 
			
		||||
$("#addriven-fingerprint").on("input", () => {
 | 
			
		||||
    $("#addriven-fingerprint").removeClass("is-invalid");
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function setFingerprint(ItemType, ItemId, fingerprint) {
 | 
			
		||||
    revalidateAuthz(() => {
 | 
			
		||||
        $.post({
 | 
			
		||||
            url: "/api/artifacts.php?" + window.authz,
 | 
			
		||||
            contentType: "text/plain",
 | 
			
		||||
            data: JSON.stringify({
 | 
			
		||||
                Upgrade: {
 | 
			
		||||
                    ItemType,
 | 
			
		||||
                    ItemId,
 | 
			
		||||
                    UpgradeFingerprint: JSON.stringify(fingerprint)
 | 
			
		||||
                },
 | 
			
		||||
                LevelDiff: 0,
 | 
			
		||||
                Cost: 0,
 | 
			
		||||
                FusionPointCost: 0
 | 
			
		||||
            })
 | 
			
		||||
        }).done(function () {
 | 
			
		||||
            updateInventory();
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function doAcquireMod() {
 | 
			
		||||
    const uniqueName = getKey(document.getElementById("mod-to-acquire"));
 | 
			
		||||
    if (!uniqueName) {
 | 
			
		||||
        $("#mod-to-acquire").addClass("is-invalid").focus();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    revalidateAuthz(() => {
 | 
			
		||||
        $.post({
 | 
			
		||||
            url: "/api/missionInventoryUpdate.php?" + window.authz,
 | 
			
		||||
            contentType: "text/plain",
 | 
			
		||||
            data: JSON.stringify({
 | 
			
		||||
                RawUpgrades: [
 | 
			
		||||
                    {
 | 
			
		||||
                        ItemType: uniqueName,
 | 
			
		||||
                        ItemCount: 1
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            })
 | 
			
		||||
        }).done(function () {
 | 
			
		||||
            document.getElementById("mod-to-acquire").value = "";
 | 
			
		||||
            updateInventory();
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$("#mod-to-acquire").on("input", () => {
 | 
			
		||||
    $("#mod-to-acquire").removeClass("is-invalid");
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,13 @@
 | 
			
		||||
@media (min-width: 992px) {
 | 
			
		||||
    body.logged-in #main-view {
 | 
			
		||||
        display: grid;
 | 
			
		||||
        grid-template-columns: 1fr 8fr;
 | 
			
		||||
        gap: 1.5rem;
 | 
			
		||||
        display: flex;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.logged-in #sidebar {
 | 
			
		||||
        position: sticky;
 | 
			
		||||
        top: 5rem;
 | 
			
		||||
        height: 100px;
 | 
			
		||||
        margin-right: 3rem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body:not(.logged-in) #sidebar {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user