diff --git a/api/main.proto b/api/main.proto index 2c28023..9127c09 100644 --- a/api/main.proto +++ b/api/main.proto @@ -136,9 +136,10 @@ message TeamAdvanced { } message Application { - int64 id = 1; - string name = 2; - string state = 3; + int64 id = 1; + string name = 2; + string state = 3; + string number = 4; } message Door { diff --git a/cmd/evening_detective/static/user/assets/index-BJOT4xui.css b/cmd/evening_detective/static/user/assets/index-CsH95clG.css similarity index 74% rename from cmd/evening_detective/static/user/assets/index-BJOT4xui.css rename to cmd/evening_detective/static/user/assets/index-CsH95clG.css index 9c8c151..cc2ac3f 100644 --- a/cmd/evening_detective/static/user/assets/index-BJOT4xui.css +++ b/cmd/evening_detective/static/user/assets/index-CsH95clG.css @@ -1 +1 @@ -:root{--vt-c-white: #ffffff;--vt-c-white-soft: #f8f8f8;--vt-c-white-mute: #f2f2f2;--vt-c-black: #181818;--vt-c-black-soft: #222222;--vt-c-black-mute: #282828;--vt-c-indigo: #2c3e50;--vt-c-divider-light-1: rgba(60, 60, 60, .29);--vt-c-divider-light-2: rgba(60, 60, 60, .12);--vt-c-divider-dark-1: rgba(84, 84, 84, .65);--vt-c-divider-dark-2: rgba(84, 84, 84, .48);--vt-c-text-light-1: var(--vt-c-indigo);--vt-c-text-light-2: rgba(60, 60, 60, .66);--vt-c-text-dark-1: var(--vt-c-white);--vt-c-text-dark-2: rgba(235, 235, 235, .64);--main-color: rgba(34, 50, 60, 1);--second-color: rgb(97, 74, 22);--main-back-color: rgba(240, 240, 240, 1);--main-back-item-color: rgba(254, 254, 254, 1)}:root{--color-background: var(--vt-c-white);--color-background-soft: var(--vt-c-white-soft);--color-background-mute: var(--vt-c-white-mute);--color-border: var(--vt-c-divider-light-2);--color-border-hover: var(--vt-c-divider-light-1);--color-heading: var(--vt-c-text-light-1);--color-text: var(--vt-c-text-light-1);--section-gap: 160px}*,*:before,*:after{box-sizing:border-box;margin:0;font-weight:400}body{min-height:100dvh;color:var(--color-text);background:var(--main-back-color);transition:color .5s,background-color .5s;line-height:1.6;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:15px;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.header-block{height:60px;background-color:var(--main-color);font-size:large;color:#fff;vertical-align:middle;padding:15px 0 10px 16px;font-weight:700}.input-custom{width:100%;box-sizing:border-box;margin-bottom:15px}.button-custom{margin-left:auto;background-color:var(--main-color);font-weight:600;color:#fff}.button-custom:hover{background-color:var(--main-color);opacity:.9}.button-custom:disabled{opacity:.5}.button-dialog{background-color:var(--main-color);font-weight:600;color:#fff;padding:6px 14px;border:1px solid #ddd;border-radius:15px;font-size:16px;margin-right:6px}.button-dialog:hover{background-color:var(--main-color);opacity:.9}.button-dialog:disabled{opacity:.5}.input-custom,.button-custom{padding:12px 16px;border:1px solid #ddd;border-radius:15px;font-size:16px}.button-container{display:flex}.center-message{display:flex;justify-content:center;align-items:center;height:calc(100dvh - 100px);text-align:center}header[data-v-913ef6b1]{line-height:1.5;max-height:100vh}.logo[data-v-913ef6b1]{display:block;margin:0 auto 2rem}nav[data-v-913ef6b1]{width:100%;font-size:12px;text-align:center;margin-top:2rem}nav a.router-link-exact-active[data-v-913ef6b1]{color:var(--color-text)}nav a.router-link-exact-active[data-v-913ef6b1]:hover{background-color:transparent}nav a[data-v-913ef6b1]{display:inline-block;padding:0 1rem;border-left:1px solid var(--color-border)}nav a[data-v-913ef6b1]:first-of-type{border:0}@media (min-width: 1024px){header[data-v-913ef6b1]{display:flex;place-items:center;padding-right:calc(var(--section-gap) / 2)}.logo[data-v-913ef6b1]{margin:0 2rem 0 0}header .wrapper[data-v-913ef6b1]{display:flex;place-items:flex-start;flex-wrap:wrap}nav[data-v-913ef6b1]{text-align:left;margin-left:-1rem;font-size:1rem;padding:1rem 0;margin-top:1rem}}body[data-v-127d6c85]{overflow:hidden}.hr[data-v-127d6c85]{margin:7px 0}.body-custom[data-v-127d6c85]{font-size:medium}.info-custom[data-v-127d6c85]{padding-left:15px}.logo[data-v-127d6c85]{float:left;margin:10px}.logo-right[data-v-127d6c85]{float:right;margin:12px}.second-color[data-v-127d6c85]{color:var(--second-color)}.form-custom[data-v-127d6c85]{border:1px solid #444444;background-color:var(--main-back-color);position:fixed;bottom:0;left:0;width:100%;padding:20px;color:#fff}.message-cloud[data-v-127d6c85]{border:1px solid #444444;border-radius:15px;margin:12px 10px;padding:16px;background-color:var(--main-back-item-color);display:flow-root}.message-header[data-v-127d6c85]{font-size:large;font-weight:200}.message-content[data-v-127d6c85]{font-weight:500;white-space:pre-wrap}.message-image[data-v-127d6c85]{width:40%;float:left;margin-right:15px}.message-footer[data-v-127d6c85]{font-weight:400;color:var(--second-color)}.form-block[data-v-127d6c85]{height:140px}.messages-block[data-v-127d6c85]{height:calc(100dvh - 200px);overflow-y:auto;scrollbar-width:none}@media (min-width: 1025px){.center-block-custom[data-v-127d6c85]{width:700px;margin:0 auto}}.center-message[data-v-127d6c85]{height:calc(100dvh - 140px)}.qr[data-v-127d6c85]{text-align:center;width:200px}.collapse-icon[data-v-127d6c85]{padding:0 15px;cursor:pointer}.team-name-block[data-v-127d6c85]{float:right;padding:0 20px}.text-truncate[data-v-127d6c85]{width:100px;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:2px 7px;margin:0 20px;background:#284557;border-radius:4px;font-size:medium}.error-message[data-v-13746d20]{color:brown;margin:16px 0} +:root{--vt-c-white: #ffffff;--vt-c-white-soft: #f8f8f8;--vt-c-white-mute: #f2f2f2;--vt-c-black: #181818;--vt-c-black-soft: #222222;--vt-c-black-mute: #282828;--vt-c-indigo: #2c3e50;--vt-c-divider-light-1: rgba(60, 60, 60, .29);--vt-c-divider-light-2: rgba(60, 60, 60, .12);--vt-c-divider-dark-1: rgba(84, 84, 84, .65);--vt-c-divider-dark-2: rgba(84, 84, 84, .48);--vt-c-text-light-1: var(--vt-c-indigo);--vt-c-text-light-2: rgba(60, 60, 60, .66);--vt-c-text-dark-1: var(--vt-c-white);--vt-c-text-dark-2: rgba(235, 235, 235, .64);--main-color: rgba(34, 50, 60, 1);--second-color: rgb(97, 74, 22);--main-back-color: rgba(240, 240, 240, 1);--main-back-item-color: rgba(254, 254, 254, 1)}:root{--color-background: var(--vt-c-white);--color-background-soft: var(--vt-c-white-soft);--color-background-mute: var(--vt-c-white-mute);--color-border: var(--vt-c-divider-light-2);--color-border-hover: var(--vt-c-divider-light-1);--color-heading: var(--vt-c-text-light-1);--color-text: var(--vt-c-text-light-1);--section-gap: 160px}*,*:before,*:after{box-sizing:border-box;margin:0;font-weight:400}body{min-height:100dvh;color:var(--color-text);background:var(--main-back-color);transition:color .5s,background-color .5s;line-height:1.6;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:15px;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.header-block{height:60px;background-color:var(--main-color);font-size:large;color:#fff;vertical-align:middle;padding:15px 0 10px 16px;font-weight:700}.input-custom{width:100%;box-sizing:border-box;margin-bottom:15px}.button-custom{margin-left:auto;background-color:var(--main-color);font-weight:600;color:#fff}.button-custom:hover{background-color:var(--main-color);opacity:.9}.button-custom:disabled{opacity:.5}.button-dialog{background-color:var(--main-color);font-weight:600;color:#fff;padding:6px 14px;border:1px solid #ddd;border-radius:15px;font-size:16px;margin-right:6px}.button-dialog:hover{background-color:var(--main-color);opacity:.9}.button-dialog:disabled{opacity:.5}.input-custom,.button-custom{padding:12px 16px;border:1px solid #ddd;border-radius:15px;font-size:16px}.button-container{display:flex}.center-message{display:flex;justify-content:center;align-items:center;height:calc(100dvh - 100px);text-align:center}header[data-v-913ef6b1]{line-height:1.5;max-height:100vh}.logo[data-v-913ef6b1]{display:block;margin:0 auto 2rem}nav[data-v-913ef6b1]{width:100%;font-size:12px;text-align:center;margin-top:2rem}nav a.router-link-exact-active[data-v-913ef6b1]{color:var(--color-text)}nav a.router-link-exact-active[data-v-913ef6b1]:hover{background-color:transparent}nav a[data-v-913ef6b1]{display:inline-block;padding:0 1rem;border-left:1px solid var(--color-border)}nav a[data-v-913ef6b1]:first-of-type{border:0}@media (min-width: 1024px){header[data-v-913ef6b1]{display:flex;place-items:center;padding-right:calc(var(--section-gap) / 2)}.logo[data-v-913ef6b1]{margin:0 2rem 0 0}header .wrapper[data-v-913ef6b1]{display:flex;place-items:flex-start;flex-wrap:wrap}nav[data-v-913ef6b1]{text-align:left;margin-left:-1rem;font-size:1rem;padding:1rem 0;margin-top:1rem}}body[data-v-95a9ee2d]{overflow:hidden}.hr[data-v-95a9ee2d]{margin:7px 0}.body-custom[data-v-95a9ee2d]{font-size:medium}.info-custom[data-v-95a9ee2d]{padding-left:15px}.logo[data-v-95a9ee2d]{float:left;margin:10px}.logo-right[data-v-95a9ee2d]{float:right;margin:12px}.second-color[data-v-95a9ee2d]{color:var(--second-color)}.form-custom[data-v-95a9ee2d]{border:1px solid #444444;background-color:var(--main-back-color);position:fixed;bottom:0;left:0;width:100%;padding:20px;color:#fff}.message-cloud[data-v-95a9ee2d]{border:1px solid #444444;border-radius:15px;margin:12px 10px;padding:16px;background-color:var(--main-back-item-color);display:flow-root}.message-header[data-v-95a9ee2d]{font-size:large;font-weight:200}.message-content[data-v-95a9ee2d]{font-weight:500;white-space:pre-wrap}.message-image[data-v-95a9ee2d]{width:40%;float:left;margin-right:15px}.message-footer[data-v-95a9ee2d]{font-weight:400;color:var(--second-color)}.form-block[data-v-95a9ee2d]{height:140px}.messages-block[data-v-95a9ee2d]{height:calc(100dvh - 200px);overflow-y:auto;scrollbar-width:none}@media (min-width: 1025px){.center-block-custom[data-v-95a9ee2d]{width:700px;margin:0 auto}}.center-message[data-v-95a9ee2d]{height:calc(100dvh - 140px)}.qr[data-v-95a9ee2d]{text-align:center;width:200px}.collapse-icon[data-v-95a9ee2d]{padding:0 15px;cursor:pointer}.team-name-block[data-v-95a9ee2d]{float:right;padding:0 20px}.text-truncate[data-v-95a9ee2d]{width:100px;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:2px 7px;margin:0 20px;background:#284557;border-radius:4px;font-size:medium}.error-message[data-v-13746d20]{color:brown;margin:16px 0} diff --git a/cmd/evening_detective/static/user/assets/index-bGblozls.js b/cmd/evening_detective/static/user/assets/index-DO4eu8bM.js similarity index 97% rename from cmd/evening_detective/static/user/assets/index-bGblozls.js rename to cmd/evening_detective/static/user/assets/index-DO4eu8bM.js index 65c973d..0162c81 100644 --- a/cmd/evening_detective/static/user/assets/index-bGblozls.js +++ b/cmd/evening_detective/static/user/assets/index-DO4eu8bM.js @@ -29,4 +29,4 @@ Make sure your charset is UTF-8`);o=(o>>>8&255)*192+(o&255),r.put(o,13)}},Pr=n,P The chosen QR Code version cannot contain this amount of data. Minimum version required to store current data is: `+j+`. `);const q=R(N,x,V),H=e.getSymbolSize(N),W=new r(H);return g(W,N),w(W),C(W,N),P(W,x,0),N>=7&&U(W,N),A(W,q),isNaN(F)&&(F=i.getBestMask(W,P.bind(null,W,x))),i.applyMask(F,W),P(W,x,F),{modules:W,version:N,errorCorrectionLevel:x,maskPattern:F,segments:V}}return ur.create=function(N,x){if(typeof N>"u"||N==="")throw new Error("No input text");let F=t.M,V,j;return typeof x<"u"&&(F=t.from(x.errorCorrectionLevel,t.M),V=f.from(x.version),j=i.from(x.maskPattern),x.toSJISFunc&&e.setToSJISFunction(x.toSJISFunc)),k(N,V,F,j)},ur}var Tr={},xr={},ko;function ml(){return ko||(ko=1,function(e){function t(n){if(typeof n=="number"&&(n=n.toString()),typeof n!="string")throw new Error("Color should be defined as hex string");let r=n.slice().replace("#","").split("");if(r.length<3||r.length===5||r.length>8)throw new Error("Invalid hex color: "+n);(r.length===3||r.length===4)&&(r=Array.prototype.concat.apply([],r.map(function(o){return[o,o]}))),r.length===6&&r.push("F","F");const s=parseInt(r.join(""),16);return{r:s>>24&255,g:s>>16&255,b:s>>8&255,a:s&255,hex:"#"+r.slice(0,6).join("")}}e.getOptions=function(r){r||(r={}),r.color||(r.color={});const s=typeof r.margin>"u"||r.margin===null||r.margin<0?4:r.margin,o=r.width&&r.width>=21?r.width:void 0,i=r.scale||4;return{width:o,scale:o?4:i,margin:s,color:{dark:t(r.color.dark||"#000000ff"),light:t(r.color.light||"#ffffffff")},type:r.type,rendererOpts:r.rendererOpts||{}}},e.getScale=function(r,s){return s.width&&s.width>=r+s.margin*2?s.width/(r+s.margin*2):s.scale},e.getImageWidth=function(r,s){const o=e.getScale(r,s);return Math.floor((r+s.margin*2)*o)},e.qrToImageData=function(r,s,o){const i=s.modules.size,c=s.modules.data,l=e.getScale(i,o),f=Math.floor((i+o.margin*2)*l),u=o.margin*l,a=[o.color.light,o.color.dark];for(let p=0;p=u&&g>=u&&p"u"&&(!i||!i.getContext)&&(l=i,i=void 0),i||(f=r()),l=t.getOptions(l);const u=t.getImageWidth(o.modules.size,l),a=f.getContext("2d"),p=a.createImageData(u,u);return t.qrToImageData(p.data,o,l),n(a,f,u),a.putImageData(p,0,0),f},e.renderToDataURL=function(o,i,c){let l=c;typeof l>"u"&&(!i||!i.getContext)&&(l=i,i=void 0),l||(l={});const f=e.render(o,i,l),u=l.type||"image/png",a=l.rendererOpts||{};return f.toDataURL(u,a.quality)}}(Tr)),Tr}var Ir={},Ho;function la(){if(Ho)return Ir;Ho=1;const e=ml();function t(s,o){const i=s.a/255,c=o+'="'+s.hex+'"';return i<1?c+" "+o+'-opacity="'+i.toFixed(2).slice(1)+'"':c}function n(s,o,i){let c=s+o;return typeof i<"u"&&(c+=" "+i),c}function r(s,o,i){let c="",l=0,f=!1,u=0;for(let a=0;a0&&p>0&&s[a-1]||(c+=f?n("M",p+i,.5+g+i):n("m",l,0),l=0,f=!1),p+1':"",g="',w='viewBox="0 0 '+a+" "+a+'"',U=''+p+g+` -`;return typeof c=="function"&&c(null,U),U},Ir}var jo;function ca(){if(jo)return xt;jo=1;const e=jf(),t=oa(),n=ia(),r=la();function s(o,i,c,l,f){const u=[].slice.call(arguments,1),a=u.length,p=typeof u[a-1]=="function";if(!p&&!e())throw new Error("Callback required as last argument");if(p){if(a<2)throw new Error("Too few arguments provided");a===2?(f=c,c=i,i=l=void 0):a===3&&(i.getContext&&typeof f>"u"?(f=l,l=void 0):(f=l,l=c,c=i,i=void 0))}else{if(a<1)throw new Error("Too few arguments provided");return a===1?(c=i,i=l=void 0):a===2&&!i.getContext&&(l=c,c=i,i=void 0),new Promise(function(g,w){try{const C=t.create(c,l);g(o(C,i,l))}catch(C){w(C)}})}try{const g=t.create(c,l);f(null,o(g,i,l))}catch(g){f(g)}}return xt.create=t.create,xt.toCanvas=s.bind(null,n.render),xt.toDataURL=s.bind(null,n.renderToDataURL),xt.toString=s.bind(null,function(o,i,c){return r.render(o,c)}),xt}var Mr=ca();/*! vue-qrcode v2.0.0 | (c) 2018-present Chen Fengyuan | MIT */const Vo="ready";var ua=vt({name:"VueQrcode",props:{value:{type:String,default:void 0},options:{type:Object,default:void 0},tag:{type:String,default:"canvas"}},emits:[Vo],watch:{$props:{deep:!0,immediate:!0,handler(){this.$el&&this.generate()}}},mounted(){this.generate()},methods:{generate(){const e=this.options||{},t=String(this.value),n=()=>{this.$emit(Vo,this.$el)};switch(this.tag){case"canvas":Mr.toCanvas(this.$el,t,e,r=>{if(r)throw r;n()});break;case"img":Mr.toDataURL(t,e,(r,s)=>{if(r)throw r;this.$el.src=s,this.$el.onload=n});break;case"svg":Mr.toString(t,e,(r,s)=>{if(r)throw r;const o=document.createElement("div");o.innerHTML=s;const i=o.querySelector("svg");if(i){const{attributes:c,childNodes:l}=i;Object.keys(c).forEach(f=>{const u=c[Number(f)];this.$el.setAttribute(u.name,u.value)}),Object.keys(l).forEach(f=>{const u=l[Number(f)];this.$el.appendChild(u.cloneNode(!0))}),n()}});break}}},render(){return ps(this.tag,this.$slots.default)}});const fa={class:"body-custom"},aa={class:"header-block"},da={class:"team-name-block text-truncate"},ha={class:"form-custom form-block"},pa={class:"center-block-custom"},ga=["disabled"],ma={class:"button-container"},ya={class:"second-color info-custom"},va=["disabled"],_a={class:"center-block-custom"},ba={key:0},wa={class:"center-message"},Ea={class:"qr"},Ca={key:1},Sa={class:"message-cloud"},Ra={class:"message-header"},Pa=["onClick"],Aa={class:"message-content"},Ta={key:0},xa=["src"],Ia={key:0,class:"hr"},Ma=["onClick","disabled"],Na={key:1,class:"hr"},Oa=vt({__name:"GameWindow",setup(e){const t=dl(),n=Uf(),r=pe(!1),s=pe(""),o=pe(""),i=pe(""),c=pe({name:"",actions:[]}),l=pe([]),f=pe(),u=pe("STOP"),a=pe(""),p=pe("-"),g=pe({width:200,margin:1,color:{dark:"#303030",light:"f0f0f0"}});function w(){fetch(En("/team"),{method:"GET",headers:{"X-Id":Gr(s.value),"X-Password":o.value}}).then(y=>{if(y.status==401){t.push("/login");return}return y.json()}).then(y=>{var N;const k=c.value.actions;c.value=y;const S=(N=c.value)==null?void 0:N.actions;S.forEach(x=>{x.isOpen=!0});for(let x=0;xV.show)}}).catch(y=>{console.error("Ошибка:",y)})}function C(){r.value=!0;const y=i.value.trim();if(y===""){i.value="";return}U(y),i.value=""}function U(y){console.log("letsgo to "+y),fetch(En("/team/actions"),{method:"POST",headers:{"X-Id":Gr(s.value),"X-Password":o.value},body:JSON.stringify({place:y})})}const P=async(y="smooth")=>{await cs(),f.value&&f.value.scrollTo({top:f.value.scrollHeight,behavior:y})};function A(){p.value=location.href,fetch(En("/game")).then(y=>y.json()).then(y=>{u.value=y.state,y.state==="NEW"&&(a.value="Игра ещё не началась"),y.state==="RUN"&&(a.value=""),y.state==="STOP"&&(a.value="Игра остановлена")}).catch(y=>{console.error("Ошибка:",y)})}Zt(l,()=>{r.value!==!1&&(P(),r.value=!1)},{deep:!0});let R=0;return as(()=>{var y,k;s.value=sessionStorage.getItem("teamId")||"",o.value=sessionStorage.getItem("password")||"",s.value==""&&(s.value=((y=n.query.name)==null?void 0:y.toString())||"",o.value=((k=n.query.password)==null?void 0:k.toString())||"",sessionStorage.setItem("teamId",s.value),sessionStorage.setItem("password",o.value)),w(),R=setInterval(()=>{w(),A()},2e3),t.beforeEach((S,N,x)=>{clearInterval(R),x()})}),(y,k)=>(he(),xe("div",fa,[k[4]||(k[4]=Q("img",{alt:"Вечерний детектив",class:"logo",src:Hf,width:"40",height:"40"},null,-1)),Q("div",aa,[k[1]||(k[1]=bn(" Вечерний детектив ")),Q("span",da,Ue(c.value.name),1)]),Q("div",ha,[Q("div",pa,[Q("form",{onSubmit:Xi(C,["prevent"])},[Q("div",null,[Pn(Q("input",{class:"input-custom","onUpdate:modelValue":k[0]||(k[0]=S=>i.value=S),type:"text",placeholder:"Место назначения (А-1, а-1, а1)",disabled:u.value!=="RUN"},null,8,ga),[[qr,i.value]])]),Q("div",ma,[Q("div",ya,Ue(a.value),1),Q("button",{class:"button-custom",type:"submit",disabled:u.value!=="RUN"},"Поехали",8,va)])],32)])]),Q("div",{class:"messages-block",ref_key:"scrollContainer",ref:f},[Q("div",_a,[!c.value||!c.value.actions.length?(he(),xe("div",ba,[Q("div",wa,[Q("div",Ea,[Pe(ht(ua),{value:p.value,options:g.value,tag:"svg",class:"qr-code"},null,8,["value","options"]),k[2]||(k[2]=Q("div",null," Пора решать загадку ",-1))])])])):(he(),xe("div",Ca,[(he(!0),xe(Ne,null,Qn(c.value.actions,S=>{var N;return he(),xe("div",{key:S.id},[Q("div",Sa,[Q("div",Ra,[bn(Ue(S.place)+": "+Ue(S.name)+" ",1),Q("span",{class:"collapse-icon",onClick:x=>S.isOpen=!S.isOpen,style:{float:"right"}},Ue(S.isOpen?"−":"+"),9,Pa)]),Pn(Q("div",null,[k[3]||(k[3]=Q("hr",{class:"hr"},null,-1)),Q("div",Aa,[S.image.length?(he(),xe("div",Ta,[Q("img",{src:S.image,class:"message-image"},null,8,xa)])):tr("",!0),bn(Ue(S.text),1)]),(N=S.buttons)!=null&&N.length?(he(),xe("hr",Ia)):tr("",!0),(he(!0),xe(Ne,null,Qn(S.buttons,x=>(he(),xe("button",{key:x.code,class:"button-dialog",onClick:F=>U(x.code),disabled:u.value!=="RUN"||!x.show},Ue(x.name),9,Ma))),128)),S.applications.length?(he(),xe("hr",Na)):tr("",!0),(he(!0),xe(Ne,null,Qn(S.applications,x=>(he(),xe("div",{class:"message-footer",key:x.name}," Приложение: "+Ue(x.name),1))),128))],512),[[au,S.isOpen]])])])}),128))]))])],512)]))}}),Ba=ys(Oa,[["__scopeId","data-v-127d6c85"]]),La=vt({__name:"HomeView",setup(e){return(t,n)=>(he(),jn(Ba))}}),Da={class:"center-message"},Fa={class:"button-container"},Ua={class:"button-custom",type:"submit"},ka={class:"error-message"},$a=vt({__name:"LoginWindow",setup(e){const t=dl(),n=pe(""),r=pe(""),s=pe("Вход"),o=pe("");function i(){const c=s.value;s.value="Загрузка...",o.value="",fetch(En("/team"),{method:"GET",headers:{"X-Id":Gr(n.value),"X-Password":r.value}}).then(l=>{if(l.status==200){sessionStorage.setItem("teamId",n.value),sessionStorage.setItem("password",r.value),t.push("/");return}if(l.status==401){if(n.value==""&&r.value=="")return;o.value="Не верны название команды или пароль";return}o.value="ХЗ что это "+l}).catch(()=>{o.value="Сервер не доступен"}).finally(()=>{s.value=c})}return as(()=>{n.value=sessionStorage.getItem("teamId")||"",r.value=sessionStorage.getItem("password")||"",i()}),(c,l)=>(he(),xe(Ne,null,[l[2]||(l[2]=Q("div",{class:"header-block"}," Вечерний детектив ",-1)),Q("div",Da,[Q("form",{onSubmit:Xi(i,["prevent"])},[Q("div",null,[Pn(Q("input",{class:"input-custom","onUpdate:modelValue":l[0]||(l[0]=f=>n.value=f),type:"text",placeholder:"Название команды"},null,512),[[qr,n.value]])]),Q("div",null,[Pn(Q("input",{class:"input-custom","onUpdate:modelValue":l[1]||(l[1]=f=>r.value=f),type:"text",placeholder:"Пароль",autocapitalize:"off"},null,512),[[qr,r.value]])]),Q("div",Fa,[Q("button",Ua,Ue(s.value),1)]),Q("div",ka,Ue(o.value),1)],32)])],64))}}),Ha=ys($a,[["__scopeId","data-v-13746d20"]]),ja=vt({__name:"LoginView",setup(e){return(t,n)=>(he(),jn(Ha))}}),Va=Df({history:df("/"),routes:[{path:"/",name:"home",component:La},{path:"/login",name:"login",component:ja}]}),_s=Iu($f);_s.use(Bu());_s.use(Va);_s.mount("#app"); +`;return typeof c=="function"&&c(null,U),U},Ir}var jo;function ca(){if(jo)return xt;jo=1;const e=jf(),t=oa(),n=ia(),r=la();function s(o,i,c,l,f){const u=[].slice.call(arguments,1),a=u.length,p=typeof u[a-1]=="function";if(!p&&!e())throw new Error("Callback required as last argument");if(p){if(a<2)throw new Error("Too few arguments provided");a===2?(f=c,c=i,i=l=void 0):a===3&&(i.getContext&&typeof f>"u"?(f=l,l=void 0):(f=l,l=c,c=i,i=void 0))}else{if(a<1)throw new Error("Too few arguments provided");return a===1?(c=i,i=l=void 0):a===2&&!i.getContext&&(l=c,c=i,i=void 0),new Promise(function(g,w){try{const C=t.create(c,l);g(o(C,i,l))}catch(C){w(C)}})}try{const g=t.create(c,l);f(null,o(g,i,l))}catch(g){f(g)}}return xt.create=t.create,xt.toCanvas=s.bind(null,n.render),xt.toDataURL=s.bind(null,n.renderToDataURL),xt.toString=s.bind(null,function(o,i,c){return r.render(o,c)}),xt}var Mr=ca();/*! vue-qrcode v2.0.0 | (c) 2018-present Chen Fengyuan | MIT */const Vo="ready";var ua=vt({name:"VueQrcode",props:{value:{type:String,default:void 0},options:{type:Object,default:void 0},tag:{type:String,default:"canvas"}},emits:[Vo],watch:{$props:{deep:!0,immediate:!0,handler(){this.$el&&this.generate()}}},mounted(){this.generate()},methods:{generate(){const e=this.options||{},t=String(this.value),n=()=>{this.$emit(Vo,this.$el)};switch(this.tag){case"canvas":Mr.toCanvas(this.$el,t,e,r=>{if(r)throw r;n()});break;case"img":Mr.toDataURL(t,e,(r,s)=>{if(r)throw r;this.$el.src=s,this.$el.onload=n});break;case"svg":Mr.toString(t,e,(r,s)=>{if(r)throw r;const o=document.createElement("div");o.innerHTML=s;const i=o.querySelector("svg");if(i){const{attributes:c,childNodes:l}=i;Object.keys(c).forEach(f=>{const u=c[Number(f)];this.$el.setAttribute(u.name,u.value)}),Object.keys(l).forEach(f=>{const u=l[Number(f)];this.$el.appendChild(u.cloneNode(!0))}),n()}});break}}},render(){return ps(this.tag,this.$slots.default)}});const fa={class:"body-custom"},aa={class:"header-block"},da={class:"team-name-block text-truncate"},ha={class:"form-custom form-block"},pa={class:"center-block-custom"},ga=["disabled"],ma={class:"button-container"},ya={class:"second-color info-custom"},va=["disabled"],_a={class:"center-block-custom"},ba={key:0},wa={class:"center-message"},Ea={class:"qr"},Ca={key:1},Sa={class:"message-cloud"},Ra={class:"message-header"},Pa=["onClick"],Aa={class:"message-content"},Ta={key:0},xa=["src"],Ia={key:0,class:"hr"},Ma=["onClick","disabled"],Na={key:1,class:"hr"},Oa=vt({__name:"GameWindow",setup(e){const t=dl(),n=Uf(),r=pe(!1),s=pe(""),o=pe(""),i=pe(""),c=pe({name:"",actions:[]}),l=pe([]),f=pe(),u=pe("STOP"),a=pe(""),p=pe("-"),g=pe({width:200,margin:1,color:{dark:"#303030",light:"f0f0f0"}});function w(){fetch(En("/team"),{method:"GET",headers:{"X-Id":Gr(s.value),"X-Password":o.value}}).then(y=>{if(y.status==401){t.push("/login");return}return y.json()}).then(y=>{var N;const k=c.value.actions;c.value=y;const S=(N=c.value)==null?void 0:N.actions;S.forEach(x=>{x.isOpen=!0});for(let x=0;xV.show)}}).catch(y=>{console.error("Ошибка:",y)})}function C(){r.value=!0;const y=i.value.trim();if(y===""){i.value="";return}U(y),i.value=""}function U(y){console.log("letsgo to "+y),fetch(En("/team/actions"),{method:"POST",headers:{"X-Id":Gr(s.value),"X-Password":o.value},body:JSON.stringify({place:y})})}const P=async(y="smooth")=>{await cs(),f.value&&f.value.scrollTo({top:f.value.scrollHeight,behavior:y})};function A(){p.value=location.href,fetch(En("/game")).then(y=>y.json()).then(y=>{u.value=y.state,y.state==="NEW"&&(a.value="Игра ещё не началась"),y.state==="RUN"&&(a.value=""),y.state==="STOP"&&(a.value="Игра остановлена")}).catch(y=>{console.error("Ошибка:",y)})}Zt(l,()=>{r.value!==!1&&(P(),r.value=!1)},{deep:!0});let R=0;return as(()=>{var y,k;s.value=sessionStorage.getItem("teamId")||"",o.value=sessionStorage.getItem("password")||"",s.value==""&&(s.value=((y=n.query.name)==null?void 0:y.toString())||"",o.value=((k=n.query.password)==null?void 0:k.toString())||"",sessionStorage.setItem("teamId",s.value),sessionStorage.setItem("password",o.value)),w(),R=setInterval(()=>{w(),A()},2e3),t.beforeEach((S,N,x)=>{clearInterval(R),x()})}),(y,k)=>(he(),xe("div",fa,[k[4]||(k[4]=Q("img",{alt:"Вечерний детектив",class:"logo",src:Hf,width:"40",height:"40"},null,-1)),Q("div",aa,[k[1]||(k[1]=bn(" Вечерний детектив ")),Q("span",da,Ue(c.value.name),1)]),Q("div",ha,[Q("div",pa,[Q("form",{onSubmit:Xi(C,["prevent"])},[Q("div",null,[Pn(Q("input",{class:"input-custom","onUpdate:modelValue":k[0]||(k[0]=S=>i.value=S),type:"text",placeholder:"Место назначения (А-1, а-1, а1)",disabled:u.value!=="RUN"},null,8,ga),[[qr,i.value]])]),Q("div",ma,[Q("div",ya,Ue(a.value),1),Q("button",{class:"button-custom",type:"submit",disabled:u.value!=="RUN"},"Поехали",8,va)])],32)])]),Q("div",{class:"messages-block",ref_key:"scrollContainer",ref:f},[Q("div",_a,[!c.value||!c.value.actions.length?(he(),xe("div",ba,[Q("div",wa,[Q("div",Ea,[Pe(ht(ua),{value:p.value,options:g.value,tag:"svg",class:"qr-code"},null,8,["value","options"]),k[2]||(k[2]=Q("div",null," Пора решать загадку ",-1))])])])):(he(),xe("div",Ca,[(he(!0),xe(Ne,null,Qn(c.value.actions,S=>{var N;return he(),xe("div",{key:S.id},[Q("div",Sa,[Q("div",Ra,[bn(Ue(S.place)+": "+Ue(S.name)+" ",1),Q("span",{class:"collapse-icon",onClick:x=>S.isOpen=!S.isOpen,style:{float:"right"}},Ue(S.isOpen?"−":"+"),9,Pa)]),Pn(Q("div",null,[k[3]||(k[3]=Q("hr",{class:"hr"},null,-1)),Q("div",Aa,[S.image.length>0?(he(),xe("div",Ta,[Q("img",{src:S.image,class:"message-image"},null,8,xa)])):tr("",!0),bn(Ue(S.text),1)]),(N=S.buttons)!=null&&N.length?(he(),xe("hr",Ia)):tr("",!0),(he(!0),xe(Ne,null,Qn(S.buttons,x=>(he(),xe("button",{key:x.code,class:"button-dialog",onClick:F=>U(x.code),disabled:u.value!=="RUN"||!x.show},Ue(x.name),9,Ma))),128)),S.applications.length?(he(),xe("hr",Na)):tr("",!0),(he(!0),xe(Ne,null,Qn(S.applications,x=>(he(),xe("div",{class:"message-footer",key:x.name}," Приложение: "+Ue(x.name),1))),128))],512),[[au,S.isOpen]])])])}),128))]))])],512)]))}}),Ba=ys(Oa,[["__scopeId","data-v-95a9ee2d"]]),La=vt({__name:"HomeView",setup(e){return(t,n)=>(he(),jn(Ba))}}),Da={class:"center-message"},Fa={class:"button-container"},Ua={class:"button-custom",type:"submit"},ka={class:"error-message"},$a=vt({__name:"LoginWindow",setup(e){const t=dl(),n=pe(""),r=pe(""),s=pe("Вход"),o=pe("");function i(){const c=s.value;s.value="Загрузка...",o.value="",fetch(En("/team"),{method:"GET",headers:{"X-Id":Gr(n.value),"X-Password":r.value}}).then(l=>{if(l.status==200){sessionStorage.setItem("teamId",n.value),sessionStorage.setItem("password",r.value),t.push("/");return}if(l.status==401){if(n.value==""&&r.value=="")return;o.value="Не верны название команды или пароль";return}o.value="ХЗ что это "+l}).catch(()=>{o.value="Сервер не доступен"}).finally(()=>{s.value=c})}return as(()=>{n.value=sessionStorage.getItem("teamId")||"",r.value=sessionStorage.getItem("password")||"",i()}),(c,l)=>(he(),xe(Ne,null,[l[2]||(l[2]=Q("div",{class:"header-block"}," Вечерний детектив ",-1)),Q("div",Da,[Q("form",{onSubmit:Xi(i,["prevent"])},[Q("div",null,[Pn(Q("input",{class:"input-custom","onUpdate:modelValue":l[0]||(l[0]=f=>n.value=f),type:"text",placeholder:"Название команды"},null,512),[[qr,n.value]])]),Q("div",null,[Pn(Q("input",{class:"input-custom","onUpdate:modelValue":l[1]||(l[1]=f=>r.value=f),type:"text",placeholder:"Пароль",autocapitalize:"off"},null,512),[[qr,r.value]])]),Q("div",Fa,[Q("button",Ua,Ue(s.value),1)]),Q("div",ka,Ue(o.value),1)],32)])],64))}}),Ha=ys($a,[["__scopeId","data-v-13746d20"]]),ja=vt({__name:"LoginView",setup(e){return(t,n)=>(he(),jn(Ha))}}),Va=Df({history:df("/"),routes:[{path:"/",name:"home",component:La},{path:"/login",name:"login",component:ja}]}),_s=Iu($f);_s.use(Bu());_s.use(Va);_s.mount("#app"); diff --git a/cmd/evening_detective/static/user/index.html b/cmd/evening_detective/static/user/index.html index 36215cd..de59849 100644 --- a/cmd/evening_detective/static/user/index.html +++ b/cmd/evening_detective/static/user/index.html @@ -5,8 +5,8 @@ Вечерний детектив - - + +
diff --git a/internal/services/link/service.go b/internal/services/link/service.go index bcdad95..8a2b2b7 100644 --- a/internal/services/link/service.go +++ b/internal/services/link/service.go @@ -20,5 +20,8 @@ func (s *service) GetTeamClientLink(name string, password string) string { } func (s *service) GetImageLink(name string) string { + if len(name) == 0 { + return "" + } return fmt.Sprintf("%s/%s", s.host, name) } diff --git a/internal/services/mappers.go b/internal/services/mappers.go index 3b79fc2..5017f42 100644 --- a/internal/services/mappers.go +++ b/internal/services/mappers.go @@ -38,6 +38,7 @@ func mapPlaceToProtoAction(place *story_service_models.Place) *proto.Action { func mapStoryApplicationToProtoApplication(application *story_service_models.Application) *proto.Application { return &proto.Application{ Name: application.Name, + Number: application.Number, } } diff --git a/internal/services/story_service/models/application_dto.go b/internal/services/story_service/models/application_dto.go index 91ee4dc..c6caa60 100644 --- a/internal/services/story_service/models/application_dto.go +++ b/internal/services/story_service/models/application_dto.go @@ -1,11 +1,34 @@ package models +import "fmt" + type Application struct { - Name string `json:"name"` + Name string `json:"name"` + Number string `json:"-"` } -func NewApplication(name string) *Application { - return &Application{ +func NewApplication( + name string, + opts ...ApplicationOpt, +) *Application { + application := &Application{ Name: name, } + for _, opt := range opts { + opt(application) + } + return application +} + +type ApplicationOpt func(application *Application) error + +func WithApplicationNumber(number int) ApplicationOpt { + return func(application *Application) error { + application.SetNumber(number) + return nil + } +} + +func (a *Application) SetNumber(number int) { + a.Number = fmt.Sprintf("%d", number) } diff --git a/internal/services/story_service/service.go b/internal/services/story_service/service.go index 3068260..f76eba2 100644 --- a/internal/services/story_service/service.go +++ b/internal/services/story_service/service.go @@ -95,8 +95,21 @@ func (s *StoryService) GetPlaces(codes []string) []*models.Place { places := make([]*models.Place, 0, 100) mOpen := map[string]any{} mDeleted := map[string]any{} + applicationNumber := 1 + applicationsMap := make(map[string]interface{}, 10) for i, code := range codes { place := s.GetPlace(code) + for i, application := range place.Applications { + if _, ok := applicationsMap[application.Name]; ok { + place.Applications = append(place.Applications[:i], place.Applications[i+1:]...) + if len(place.Applications) == 0 { + place.Applications = nil + } + } + applicationsMap[application.Name] = struct{}{} + application.SetNumber(applicationNumber) + applicationNumber++ + } if _, ok := mOpen[place.Code]; place.Hidden && !ok { place = models.NewNotFoundPlace(place.Code) } diff --git a/internal/services/story_service/service_test.go b/internal/services/story_service/service_test.go index 34682fe..9508012 100644 --- a/internal/services/story_service/service_test.go +++ b/internal/services/story_service/service_test.go @@ -456,6 +456,40 @@ func TestStoryService_GetPlaces(t *testing.T) { models.NewNotFoundPlace("Ы-2"), }, }, + { + name: "Улики не повторяются", + story: &models.Story{ + Places: []*models.Place{ + models.NewPlace( + "Ы", + "Название", + "Текст", + models.WithPlaceApplication( + models.NewApplication("Название"), + ), + ), + }, + }, + codes: []string{"Ы", "Ы"}, + want: []*models.Place{ + models.NewPlace( + "Ы", + "Название", + "Текст", + models.WithPlaceApplication( + models.NewApplication( + "Название", + models.WithApplicationNumber(1), + ), + ), + ), + models.NewPlace( + "Ы", + "Название", + "Текст", + ), + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/proto/main.pb.go b/proto/main.pb.go index 5dd40e4..2030255 100644 --- a/proto/main.pb.go +++ b/proto/main.pb.go @@ -535,6 +535,7 @@ type Application struct { Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` + Number string `protobuf:"bytes,4,opt,name=number,proto3" json:"number,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -590,6 +591,13 @@ func (x *Application) GetState() string { return "" } +func (x *Application) GetNumber() string { + if x != nil { + return x.Number + } + return "" +} + type Door struct { state protoimpl.MessageState `protogen:"open.v1"` Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` @@ -1736,11 +1744,12 @@ const file_main_proto_rawDesc = "" + "\bpassword\x18\x03 \x01(\tR\bpassword\x12\x10\n" + "\x03url\x18\x04 \x01(\tR\x03url\x12\x1c\n" + "\tspendTime\x18\x05 \x01(\x03R\tspendTime\x12H\n" + - "\fapplications\x18\x06 \x03(\v2$.crabs.evening_detective.ApplicationR\fapplications\"G\n" + + "\fapplications\x18\x06 \x03(\v2$.crabs.evening_detective.ApplicationR\fapplications\"_\n" + "\vApplication\x12\x0e\n" + "\x02id\x18\x01 \x01(\x03R\x02id\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12\x14\n" + - "\x05state\x18\x03 \x01(\tR\x05state\"B\n" + + "\x05state\x18\x03 \x01(\tR\x05state\x12\x16\n" + + "\x06number\x18\x04 \x01(\tR\x06number\"B\n" + "\x04Door\x12\x12\n" + "\x04code\x18\x01 \x01(\tR\x04code\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n" + diff --git a/proto/main.swagger.json b/proto/main.swagger.json index 26861dd..1de1850 100644 --- a/proto/main.swagger.json +++ b/proto/main.swagger.json @@ -462,6 +462,9 @@ }, "state": { "type": "string" + }, + "number": { + "type": "string" } } },