This commit is contained in:
Anna 2022-09-04 03:12:51 -04:00
parent 6d6fc62af7
commit 2df42dfe17
10 changed files with 116 additions and 31 deletions

View File

@ -17,6 +17,7 @@ internal class Message {
public int PositiveVotes { get; init; }
public int NegativeVotes { get; init; }
public int UserVote { get; set; }
internal Vector3 Position => new(this.X, this.Y, this.Z);
}
@ -35,6 +36,7 @@ internal class MessageWithTerritory {
public int PositiveVotes { get; init; }
public int NegativeVotes { get; init; }
public int UserVote { get; set; }
internal Vector3 Position => new(this.X, this.Y, this.Z);
@ -48,6 +50,7 @@ internal class MessageWithTerritory {
Text = message.Text,
PositiveVotes = message.PositiveVotes,
NegativeVotes = message.NegativeVotes,
UserVote = message.UserVote,
};
}
}

View File

@ -1,6 +1,6 @@
name: Orange Guidance Tomestone
author: ascclemes
punchline: Try finger, but hole
punchline: Leave messages like in Souls games. Great chest ahead.
description: |-
Dark Souls-like messaging system.
repo_url: https://git.anna.lgbt/ascclemens/OrangeGuidanceTomestone

View File

@ -1,4 +1,5 @@
using Dalamud.Game;
using Dalamud.Data;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.Command;
using Dalamud.IoC;
@ -18,6 +19,9 @@ public class Plugin : IDalamudPlugin {
[PluginService]
internal CommandManager CommandManager { get; init; }
[PluginService]
internal DataManager DataManager { get; init; }
[PluginService]
internal Framework Framework { get; init; }

View File

@ -1,4 +1,6 @@
using Dalamud.Utility;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using Newtonsoft.Json;
using OrangeGuidanceTomestone.Helpers;
@ -23,10 +25,14 @@ internal class MessageList : ITab {
this.MessagesMutex.Wait();
foreach (var message in this.Messages) {
var territory = this.Plugin.DataManager.GetExcelSheet<TerritoryType>()?.GetRow(message.Territory);
var territoryName = territory?.PlaceName.Value?.Name?.ToDalamudString().TextValue ?? "???";
ImGui.TextUnformatted(message.Text);
ImGui.TreePush();
ImGui.TextUnformatted($"Likes: {message.PositiveVotes}");
ImGui.TextUnformatted($"Dislikes: {message.NegativeVotes}");
ImGui.TextUnformatted($"Location: {territoryName}");
var appraisals = Math.Max(0, message.PositiveVotes - message.NegativeVotes);
ImGui.TextUnformatted($"Appraisals: {appraisals} ({message.PositiveVotes} - {message.NegativeVotes}");
if (ImGui.Button($"Delete##{message.Id}")) {
this.Delete(message.Id);
}

View File

@ -77,32 +77,50 @@ internal class Viewer {
ImGui.TextUnformatted(message.Text);
ImGui.PopTextWrapPos();
ImGui.TextUnformatted($"Appraisals: {message.PositiveVotes - message.NegativeVotes}");
var appraisals = Math.Max(0, message.PositiveVotes - message.NegativeVotes);
ImGui.TextUnformatted($"Appraisals: {appraisals:N0}");
if (ImGui.Button("Like")) {
void Vote(int way) {
Task.Run(async () => {
await ServerHelper.SendRequest(
var resp = await ServerHelper.SendRequest(
this.Plugin.Config.ApiKey,
HttpMethod.Patch,
$"/messages/{message.Id}/votes",
"application/json",
new StringContent("1")
new StringContent(way.ToString())
);
if (resp.IsSuccessStatusCode) {
message.UserVote = way;
}
});
}
var vote = message.UserVote;
if (vote == 1) {
ImGui.BeginDisabled();
}
if (ImGui.Button("Like")) {
Vote(1);
}
if (vote == 1) {
ImGui.EndDisabled();
}
ImGui.SameLine();
if (vote == -1) {
ImGui.BeginDisabled();
}
if (ImGui.Button("Dislike")) {
Task.Run(async () => {
await ServerHelper.SendRequest(
this.Plugin.Config.ApiKey,
HttpMethod.Patch,
$"/messages/{message.Id}/votes",
"application/json",
new StringContent("-1")
);
});
Vote(-1);
}
if (vote == -1) {
ImGui.EndDisabled();
}
}

View File

@ -18,17 +18,6 @@ pub struct Message {
pub word_2_word: Option<usize>,
}
#[derive(Debug, Deserialize)]
pub struct VoteMessage {
pub post_id: Uuid,
pub vote: u8,
}
#[derive(Debug, Deserialize)]
pub struct DeleteMessage {
pub post_id: Uuid,
}
#[derive(Debug, Serialize)]
pub struct RetrievedMessage {
pub id: String,
@ -38,6 +27,7 @@ pub struct RetrievedMessage {
pub message: String,
pub positive_votes: i32,
pub negative_votes: i32,
pub user_vote: i64,
}
#[derive(Debug, Serialize)]
@ -50,4 +40,5 @@ pub struct RetrievedMessageTerritory {
pub message: String,
pub positive_votes: i32,
pub negative_votes: i32,
pub user_vote: i64,
}

View File

@ -15,6 +15,7 @@ mod erase;
mod get_location;
mod vote;
mod get_mine;
mod get_message;
pub fn routes(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
register::register(Arc::clone(&state))
@ -22,6 +23,7 @@ pub fn routes(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
.or(write::write(Arc::clone(&state)))
.or(erase::erase(Arc::clone(&state)))
.or(vote::vote(Arc::clone(&state)))
.or(get_message::get_message(Arc::clone(&state)))
.or(get_location::get_location(Arc::clone(&state)))
.or(get_mine::get_mine(Arc::clone(&state)))
.recover(handle_rejection)
@ -59,6 +61,7 @@ pub enum WebError {
InvalidPackId,
InvalidIndex,
TooManyMessages,
NoSuchMessage,
}
impl Reject for WebError {}
@ -78,6 +81,7 @@ async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
WebError::InvalidPackId => StatusCode::NOT_FOUND,
WebError::InvalidIndex => StatusCode::NOT_FOUND,
WebError::TooManyMessages => StatusCode::BAD_REQUEST,
WebError::NoSuchMessage => StatusCode::NOT_FOUND,
}
} else {
eprintln!("{:#?}", err);

View File

@ -20,7 +20,7 @@ pub fn get_location(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
async fn logic(state: Arc<State>, id: i64, location: u32) -> Result<impl Reply, Rejection> {
// TODO: when we're not just returning all results, make sure own messages are always present
let id = location as i64;
let location = location as i64;
let messages = sqlx::query_as!(
RetrievedMessage,
// language=sqlite
@ -31,12 +31,15 @@ async fn logic(state: Arc<State>, id: i64, location: u32) -> Result<impl Reply,
m.z,
m.message,
coalesce(sum(v.vote between 0 and 1), 0) as positive_votes,
coalesce(sum(v.vote between -1 and 0), 0) as negative_votes
coalesce(sum(v.vote between -1 and 0), 0) as negative_votes,
v2.vote as user_vote
from messages m
left join votes v on m.id = v.message
left join votes v2 on m.id = v2.message and v2.user = ?
where m.territory = ?
group by m.id"#,
id,
location,
)
.fetch_all(&state.db)
.await

View File

@ -0,0 +1,53 @@
use std::sync::Arc;
use anyhow::Context;
use uuid::Uuid;
use warp::{Filter, Rejection, Reply};
use warp::filters::BoxedFilter;
use crate::message::RetrievedMessageTerritory;
use crate::State;
use crate::web::{AnyhowRejection, WebError};
pub fn get_message(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
warp::get()
.and(warp::path("messages"))
.and(warp::path::param())
.and(warp::path::end())
.and(super::get_id(Arc::clone(&state)))
.and_then(move |message_id: Uuid, id: i64| logic(Arc::clone(&state), id, message_id))
.boxed()
}
async fn logic(state: Arc<State>, id: i64, message_id: Uuid) -> Result<impl Reply, Rejection> {
let message_id = message_id.simple().to_string();
let message = sqlx::query_as!(
RetrievedMessageTerritory,
// language=sqlite
r#"
select m.id,
m.territory,
m.x,
m.y,
m.z,
m.message,
coalesce(sum(v.vote between 0 and 1), 0) as positive_votes,
coalesce(sum(v.vote between -1 and 0), 0) as negative_votes,
v2.vote as user_vote
from messages m
left join votes v on m.id = v.message
left join votes v2 on m.id = v2.message and v2.user = ?
where m.id = ?
group by m.id"#,
id,
message_id,
)
.fetch_optional(&state.db)
.await
.context("could not get message from database")
.map_err(AnyhowRejection)
.map_err(warp::reject::custom)?;
let message = message.ok_or_else(|| warp::reject::custom(WebError::NoSuchMessage))?;
Ok(warp::reply::json(&message))
}

View File

@ -29,12 +29,15 @@ async fn logic(state: Arc<State>, id: i64) -> Result<impl Reply, Rejection> {
m.z,
m.message,
coalesce(sum(v.vote between 0 and 1), 0) as positive_votes,
coalesce(sum(v.vote between -1 and 0), 0) as negative_votes
coalesce(sum(v.vote between -1 and 0), 0) as negative_votes,
v2.vote as user_vote
from messages m
left join votes v on m.id = v.message
left join votes v2 on m.id = v2.message and v2.user = ?
where m.user = ?
group by m.id"#,
id,
id,
)
.fetch_all(&state.db)
.await