fix: properly sort duties
This commit is contained in:
parent
bb416588cd
commit
eafeb9a622
|
@ -2,18 +2,11 @@
|
||||||
let stateWasNull = false;
|
let stateWasNull = false;
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
allowed: {},
|
allowed: [],
|
||||||
centre: "All",
|
centre: "All",
|
||||||
list: null,
|
list: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const categoryDefaults = {
|
|
||||||
type: 0,
|
|
||||||
category: 0,
|
|
||||||
highEnd: false,
|
|
||||||
contentKind: -1,
|
|
||||||
};
|
|
||||||
|
|
||||||
function addJsClass() {
|
function addJsClass() {
|
||||||
document.children[0].className = 'js';
|
document.children[0].className = 'js';
|
||||||
}
|
}
|
||||||
|
@ -23,8 +16,12 @@
|
||||||
if (saved !== null) {
|
if (saved !== null) {
|
||||||
try {
|
try {
|
||||||
saved = JSON.parse(saved);
|
saved = JSON.parse(saved);
|
||||||
|
if (!Array.isArray(saved.allowed)) {
|
||||||
|
saved = {};
|
||||||
|
stateWasNull = true;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
saved = {}
|
saved = {};
|
||||||
stateWasNull = true;
|
stateWasNull = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,33 +46,16 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOptionValues(option) {
|
|
||||||
return {
|
|
||||||
type: option.dataset.type || categoryDefaults.type,
|
|
||||||
category: option.dataset.category || categoryDefaults.category,
|
|
||||||
highEnd: option.dataset.highEnd || categoryDefaults.highEnd,
|
|
||||||
contentKind: option.dataset.contentKind || categoryDefaults.contentKind,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function reflectState() {
|
function reflectState() {
|
||||||
let category = document.getElementById('category-filter');
|
let category = document.getElementById('category-filter');
|
||||||
for (let option of category.options) {
|
for (let option of category.options) {
|
||||||
let values = getOptionValues(option);
|
|
||||||
let key = `${values.type}/${values.category}/${values.highEnd}`;
|
|
||||||
if (state.allowed[key] === undefined) {
|
|
||||||
if (stateWasNull) {
|
if (stateWasNull) {
|
||||||
state.allowed[key] = [];
|
console.log('was null');
|
||||||
} else {
|
state.allowed.push(option.value);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stateWasNull) {
|
console.log(`allowed includes ${option.value} = ${state.allowed.includes(option.value)}`)
|
||||||
state.allowed[key].push(values.contentKind);
|
option.selected = state.allowed.includes(option.value);
|
||||||
}
|
|
||||||
|
|
||||||
option.selected = state.allowed[key].includes(-1) || state.allowed[key].includes(values.contentKind);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let dataCentre = document.getElementById('data-centre-filter');
|
let dataCentre = document.getElementById('data-centre-filter');
|
||||||
|
@ -96,18 +76,9 @@
|
||||||
|
|
||||||
function refilter() {
|
function refilter() {
|
||||||
function categoryFilter(item) {
|
function categoryFilter(item) {
|
||||||
let data = item.elm.dataset;
|
let category = item.elm.dataset.pfCategory;
|
||||||
let type = data.type;
|
|
||||||
let category = data.category;
|
|
||||||
let highEnd = data.highEnd;
|
|
||||||
let contentKind = data.contentKind;
|
|
||||||
|
|
||||||
let allowedContentKind = state.allowed[`${type}/${category}/${highEnd}`];
|
return category === 'unknown' || state.allowed.includes(category);
|
||||||
if (allowedContentKind === undefined) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return allowedContentKind.includes(-1) || allowedContentKind.includes(contentKind);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function dataCentreFilter(item) {
|
function dataCentreFilter(item) {
|
||||||
|
@ -156,23 +127,15 @@
|
||||||
let select = document.getElementById('category-filter');
|
let select = document.getElementById('category-filter');
|
||||||
|
|
||||||
select.addEventListener('change', () => {
|
select.addEventListener('change', () => {
|
||||||
let allowed = new Map();
|
let allowed = [];
|
||||||
|
|
||||||
for (let option of select.options) {
|
for (let option of select.options) {
|
||||||
if (!option.selected) {
|
if (!option.selected) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let values = getOptionValues(option);
|
let category = option.value;
|
||||||
|
allowed.push(category);
|
||||||
let key = `${values.type}/${values.category}/${values.highEnd}`;
|
|
||||||
if (allowed[key] === undefined) {
|
|
||||||
allowed[key] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!allowed[key].includes(values.contentKind)) {
|
|
||||||
allowed[key].push(values.contentKind);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state.allowed = allowed;
|
state.allowed = allowed;
|
||||||
|
|
115
src/listing.rs
115
src/listing.rs
|
@ -5,6 +5,7 @@ use ffxiv_types::{Role, World};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
use sestring::SeString;
|
use sestring::SeString;
|
||||||
|
use crate::ffxiv::duties::{ContentKind, DutyInfo};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
||||||
pub struct PartyFinderListing {
|
pub struct PartyFinderListing {
|
||||||
|
@ -54,6 +55,8 @@ impl PartyFinderListing {
|
||||||
}
|
}
|
||||||
(DutyType::Other, DutyCategory::TheHunt) => return Cow::from("The Hunt"),
|
(DutyType::Other, DutyCategory::TheHunt) => return Cow::from("The Hunt"),
|
||||||
(DutyType::Other, DutyCategory::Duty) if self.duty == 0 => return Cow::from("None"),
|
(DutyType::Other, DutyCategory::Duty) if self.duty == 0 => return Cow::from("None"),
|
||||||
|
(DutyType::Other, DutyCategory::DeepDungeons) if self.duty == 1 => return Cow::from("The Palace of the Dead"),
|
||||||
|
(DutyType::Other, DutyCategory::DeepDungeons) if self.duty == 2 => return Cow::from("Heaven-on-High"),
|
||||||
(DutyType::Normal, _) => {
|
(DutyType::Normal, _) => {
|
||||||
if let Some(info) = crate::ffxiv::DUTIES.get(&u32::from(self.duty)) {
|
if let Some(info) = crate::ffxiv::DUTIES.get(&u32::from(self.duty)) {
|
||||||
return Cow::from(info.name);
|
return Cow::from(info.name);
|
||||||
|
@ -168,6 +171,39 @@ impl PartyFinderListing {
|
||||||
.map(|info| info.content_kind.as_u32())
|
.map(|info| info.content_kind.as_u32())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pf_category(&self) -> Option<PartyFinderCategory> {
|
||||||
|
let duty_type = self.duty_type;
|
||||||
|
let duty_info = crate::ffxiv::DUTIES.get(&u32::from(self.duty));
|
||||||
|
let duty_category = self.category;
|
||||||
|
|
||||||
|
let category = match (duty_type, duty_info, duty_category) {
|
||||||
|
(DutyType::Other, None, _) => PartyFinderCategory::None,
|
||||||
|
(DutyType::Roulette, _, _) => PartyFinderCategory::DutyRoulette,
|
||||||
|
(DutyType::Normal, Some(DutyInfo { high_end: true, .. }), _) => PartyFinderCategory::HighEndDuty,
|
||||||
|
(DutyType::Normal, Some(DutyInfo { content_kind: ContentKind::Dungeons, .. }), _) => PartyFinderCategory::Dungeons,
|
||||||
|
(DutyType::Normal, Some(DutyInfo { content_kind: ContentKind::Guildhests, .. }), _) => PartyFinderCategory::Guildhests,
|
||||||
|
(DutyType::Normal, Some(DutyInfo { content_kind: ContentKind::Trials, .. }), _) => PartyFinderCategory::Trials,
|
||||||
|
(DutyType::Normal, Some(DutyInfo { content_kind: ContentKind::Raids, .. }), _) => PartyFinderCategory::Raids,
|
||||||
|
(DutyType::Normal, Some(DutyInfo { content_kind: ContentKind::PvP, .. }), _) => PartyFinderCategory::Pvp,
|
||||||
|
(_, _, DutyCategory::QuestBattles) => PartyFinderCategory::QuestBattles,
|
||||||
|
(_, _, DutyCategory::Fates) => PartyFinderCategory::Fates,
|
||||||
|
(_, _, DutyCategory::TreasureHunt) => PartyFinderCategory::TreasureHunt,
|
||||||
|
(_, _, DutyCategory::TheHunt) => PartyFinderCategory::TheHunt,
|
||||||
|
(DutyType::Normal, _, DutyCategory::GatheringForays) => PartyFinderCategory::GatheringForays,
|
||||||
|
(DutyType::Other, _, DutyCategory::DeepDungeons) => PartyFinderCategory::DeepDungeons,
|
||||||
|
(DutyType::Normal, _, DutyCategory::AdventuringForays) => PartyFinderCategory::AdventuringForays,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(category)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn html_pf_category(&self) -> &'static str {
|
||||||
|
self.pf_category()
|
||||||
|
.map(|cat| cat.as_str())
|
||||||
|
.unwrap_or("unknown")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq)]
|
||||||
|
@ -444,3 +480,82 @@ impl JobFlags {
|
||||||
cjs
|
cjs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
|
pub enum PartyFinderCategory {
|
||||||
|
DutyRoulette,
|
||||||
|
Dungeons,
|
||||||
|
Guildhests,
|
||||||
|
Trials,
|
||||||
|
Raids,
|
||||||
|
HighEndDuty,
|
||||||
|
Pvp,
|
||||||
|
QuestBattles,
|
||||||
|
Fates,
|
||||||
|
TreasureHunt,
|
||||||
|
TheHunt,
|
||||||
|
GatheringForays,
|
||||||
|
DeepDungeons,
|
||||||
|
AdventuringForays,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartyFinderCategory {
|
||||||
|
pub const ALL: [Self; 15] = [
|
||||||
|
Self::DutyRoulette,
|
||||||
|
Self::Dungeons,
|
||||||
|
Self::Guildhests,
|
||||||
|
Self::Trials,
|
||||||
|
Self::Raids,
|
||||||
|
Self::HighEndDuty,
|
||||||
|
Self::Pvp,
|
||||||
|
Self::QuestBattles,
|
||||||
|
Self::Fates,
|
||||||
|
Self::TreasureHunt,
|
||||||
|
Self::TheHunt,
|
||||||
|
Self::GatheringForays,
|
||||||
|
Self::DeepDungeons,
|
||||||
|
Self::AdventuringForays,
|
||||||
|
Self::None,
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::DutyRoulette => "DutyRoulette",
|
||||||
|
Self::Dungeons => "Dungeons",
|
||||||
|
Self::Guildhests => "Guildhests",
|
||||||
|
Self::Trials => "Trials",
|
||||||
|
Self::Raids => "Raids",
|
||||||
|
Self::HighEndDuty => "HighEndDuty",
|
||||||
|
Self::Pvp => "Pvp",
|
||||||
|
Self::QuestBattles => "QuestBattles",
|
||||||
|
Self::Fates => "Fates",
|
||||||
|
Self::TreasureHunt => "TreasureHunt",
|
||||||
|
Self::TheHunt => "TheHunt",
|
||||||
|
Self::GatheringForays => "GatheringForays",
|
||||||
|
Self::DeepDungeons => "DeepDungeons",
|
||||||
|
Self::AdventuringForays => "AdventuringForays",
|
||||||
|
Self::None => "None",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::DutyRoulette => "Duty Roulette",
|
||||||
|
Self::Dungeons => "Dungeons",
|
||||||
|
Self::Guildhests => "Guildhests",
|
||||||
|
Self::Trials => "Trials",
|
||||||
|
Self::Raids => "Raids",
|
||||||
|
Self::HighEndDuty => "High-end Duty",
|
||||||
|
Self::Pvp => "PvP",
|
||||||
|
Self::QuestBattles => "Quest Battles",
|
||||||
|
Self::Fates => "FATEs",
|
||||||
|
Self::TreasureHunt => "Treasure Hunt",
|
||||||
|
Self::TheHunt => "The Hunt",
|
||||||
|
Self::GatheringForays => "Gathering Forays",
|
||||||
|
Self::DeepDungeons => "Deep Dungeons",
|
||||||
|
Self::AdventuringForays => "Adventuring Forays",
|
||||||
|
Self::None => "None",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ pub struct ListingContainer {
|
||||||
pub struct QueriedListing {
|
pub struct QueriedListing {
|
||||||
#[serde(with = "mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime")]
|
#[serde(with = "mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime")]
|
||||||
pub updated_at: DateTime<Utc>,
|
pub updated_at: DateTime<Utc>,
|
||||||
|
#[serde(with = "mongodb::bson::serde_helpers::chrono_datetime_as_bson_datetime")]
|
||||||
|
pub updated_minute: DateTime<Utc>,
|
||||||
pub time_left: f64,
|
pub time_left: f64,
|
||||||
pub listing: PartyFinderListing,
|
pub listing: PartyFinderListing,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use askama::Template;
|
||||||
use crate::listing_container::QueriedListing;
|
use crate::listing_container::QueriedListing;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use crate::sestring_ext::SeStringExt;
|
use crate::sestring_ext::SeStringExt;
|
||||||
|
use crate::listing::PartyFinderCategory;
|
||||||
|
|
||||||
#[derive(Debug, Template)]
|
#[derive(Debug, Template)]
|
||||||
#[template(path = "listings.html")]
|
#[template(path = "listings.html")]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::convert::{Infallible, TryFrom};
|
use std::convert::{Infallible, TryFrom};
|
||||||
use anyhow::{Result, Context};
|
use anyhow::{Result, Context};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -180,6 +181,14 @@ fn listings(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
containers.sort_by(|a, b| a.time_left.partial_cmp(&b.time_left).unwrap_or(Ordering::Equal));
|
||||||
|
|
||||||
|
containers.sort_by_key(|container| container.listing.pf_category());
|
||||||
|
containers.reverse();
|
||||||
|
|
||||||
|
containers.sort_by_key(|container| container.updated_minute);
|
||||||
|
containers.reverse();
|
||||||
|
|
||||||
Ok(ListingsTemplate {
|
Ok(ListingsTemplate {
|
||||||
containers,
|
containers,
|
||||||
})
|
})
|
||||||
|
|
|
@ -41,21 +41,9 @@ Remote Party Finder
|
||||||
<label>
|
<label>
|
||||||
Categories
|
Categories
|
||||||
<select multiple id="category-filter">
|
<select multiple id="category-filter">
|
||||||
<option>None</option>
|
{%- for category in PartyFinderCategory::ALL %}
|
||||||
<option data-type="1">Duty Roulette</option>
|
<option value="{{ category.as_str() }}">{{ category.name() }}</option>
|
||||||
<option data-type="2" data-content-kind="2">Dungeons</option>
|
{%- endfor %}
|
||||||
<option data-type="2" data-content-kind="3">Guildhests</option>
|
|
||||||
<option data-type="2" data-content-kind="4">Trials</option>
|
|
||||||
<option data-type="2" data-content-kind="5">Raids</option>
|
|
||||||
<option data-type="2" data-high-end="true">High-end Duty</option>
|
|
||||||
<option data-type="2" data-content-kind="6">PvP</option>
|
|
||||||
<option data-category="1">Quest Battles</option>
|
|
||||||
<option data-category="2">FATEs</option>
|
|
||||||
<option data-category="4">Treasure Hunt</option>
|
|
||||||
<option data-category="8">The Hunt</option>
|
|
||||||
<option data-type="2" data-category="16">Gathering Forays</option>
|
|
||||||
<option data-type="2" data-category="32">Deep Dungeons</option>
|
|
||||||
<option data-type="2" data-category="64">Adventuring Forays</option>
|
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,10 +60,7 @@ Remote Party Finder
|
||||||
class="listing"
|
class="listing"
|
||||||
data-id="{{ listing.id }}"
|
data-id="{{ listing.id }}"
|
||||||
data-centre="{{ listing.data_centre_name().unwrap_or_default() }}"
|
data-centre="{{ listing.data_centre_name().unwrap_or_default() }}"
|
||||||
data-type="{{ listing.duty_type.as_u8() }}"
|
data-pf-category="{{ listing.html_pf_category() }}">
|
||||||
data-category="{{ listing.category.as_u32() }}"
|
|
||||||
data-high-end="{{ listing.high_end() }}"
|
|
||||||
data-content-kind={{ listing.content_kind() }}>
|
|
||||||
<div class="left">
|
<div class="left">
|
||||||
{%- let duty_class %}
|
{%- let duty_class %}
|
||||||
{%- if listing.is_cross_world() %}
|
{%- if listing.is_cross_world() %}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user