This is an automated email from the ASF dual-hosted git repository.

linkinstar pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git

commit 283aa092fe09c692da4d3b79331e1785047af26c
Author: hgaol <[email protected]>
AuthorDate: Thu May 2 18:18:16 2024 +0800

    update
---
 cmd/wire_gen.go                                    |  29 +---
 internal/base/reason/reason.go                     |   1 +
 internal/controller/controller.go                  |   1 +
 internal/controller/meta_controller.go             |  61 +++++++++
 internal/entity/meta_entity.go                     |   1 +
 internal/router/answer_api_router.go               |   6 +
 .../index.scss => internal/schema/meta_schema.go   |  19 +--
 internal/service/activity/activity.go              |   3 +-
 internal/service/meta/meta_service.go              | 149 ++++++++++++++++++++-
 .../Questions/Detail/components/Answer/index.tsx   |   3 +
 .../Questions/Detail/components/Question/index.tsx |   3 +
 .../Detail/components/Reactions/index.tsx          |  86 ++++++++++++
 ui/src/pages/Questions/Detail/index.scss           |  44 +++++-
 13 files changed, 364 insertions(+), 42 deletions(-)

diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go
index 9338e806..a1229717 100644
--- a/cmd/wire_gen.go
+++ b/cmd/wire_gen.go
@@ -1,28 +1,8 @@
-//go:build !wireinject
-// +build !wireinject
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
 // Code generated by Wire. DO NOT EDIT.
 
 //go:generate go run github.com/google/wire/cmd/wire
+//go:build !wireinject
+// +build !wireinject
 
 package answercmd
 
@@ -169,7 +149,7 @@ func initApplication(debug bool, serverConf *conf.Server, 
dbConf *data.Database,
        collectionCommon := collectioncommon.NewCollectionCommon(collectionRepo)
        answerCommon := answercommon.NewAnswerCommon(answerRepo)
        metaRepo := meta.NewMetaRepo(dataData)
-       metaService := meta2.NewMetaService(metaRepo)
+       metaService := meta2.NewMetaService(metaRepo, userCommon)
        questionCommon := questioncommon.NewQuestionCommon(questionRepo, 
answerRepo, voteRepo, followRepo, tagCommonService, userCommon, 
collectionCommon, answerCommon, metaService, configService, 
activityQueueService, revisionRepo, dataData)
        userService := content.NewUserService(userRepo, userActiveActivityRepo, 
activityRepo, emailService, authService, siteInfoCommonService, 
userRoleRelService, userCommon, userExternalLoginService, 
userNotificationConfigRepo, userNotificationConfigService, questionCommon)
        captchaRepo := captcha.NewCaptchaRepo(dataData)
@@ -250,7 +230,8 @@ func initApplication(debug bool, serverConf *conf.Server, 
dbConf *data.Database,
        permissionController := controller.NewPermissionController(rankService)
        userPluginController := 
controller.NewUserPluginController(pluginCommonService)
        reviewController := controller.NewReviewController(reviewService, 
rankService, captchaService)
-       answerAPIRouter := router.NewAnswerAPIRouter(langController, 
userController, commentController, reportController, voteController, 
tagController, followController, collectionController, questionController, 
answerController, searchController, revisionController, rankController, 
userAdminController, reasonController, themeController, siteInfoController, 
controllerSiteInfoController, notificationController, dashboardController, 
uploadController, activityController, roleController, pluginCon [...]
+       metaController := controller.NewMetaController(metaService)
+       answerAPIRouter := router.NewAnswerAPIRouter(langController, 
userController, commentController, reportController, voteController, 
tagController, followController, collectionController, questionController, 
answerController, searchController, revisionController, rankController, 
userAdminController, reasonController, themeController, siteInfoController, 
controllerSiteInfoController, notificationController, dashboardController, 
uploadController, activityController, roleController, pluginCon [...]
        swaggerRouter := router.NewSwaggerRouter(swaggerConf)
        uiRouter := router.NewUIRouter(controllerSiteInfoController, 
siteInfoCommonService)
        authUserMiddleware := middleware.NewAuthUserMiddleware(authService, 
siteInfoCommonService)
diff --git a/internal/base/reason/reason.go b/internal/base/reason/reason.go
index 0dbf73ac..cc30f71f 100644
--- a/internal/base/reason/reason.go
+++ b/internal/base/reason/reason.go
@@ -103,6 +103,7 @@ const (
        AddBulkUsersFormatError          = 
"error.user.add_bulk_users_format_error"
        AddBulkUsersAmountError          = 
"error.user.add_bulk_users_amount_error"
        InvalidURLError                  = "error.common.invalid_url"
+       MetaObjectNotFound               = "error.meta.object_not_found"
 )
 
 // user external login reasons
diff --git a/internal/controller/controller.go 
b/internal/controller/controller.go
index c690d4c7..4db48322 100644
--- a/internal/controller/controller.go
+++ b/internal/controller/controller.go
@@ -49,4 +49,5 @@ var ProviderSetController = wire.NewSet(
        NewUserPluginController,
        NewReviewController,
        NewCaptchaController,
+       NewMetaController,
 )
diff --git a/internal/controller/meta_controller.go 
b/internal/controller/meta_controller.go
new file mode 100644
index 00000000..de7d5ddc
--- /dev/null
+++ b/internal/controller/meta_controller.go
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package controller
+
+import (
+       "github.com/apache/incubator-answer/internal/base/handler"
+       "github.com/apache/incubator-answer/internal/base/middleware"
+       "github.com/apache/incubator-answer/internal/schema"
+       "github.com/apache/incubator-answer/internal/service/meta"
+       "github.com/gin-gonic/gin"
+)
+
+type MetaController struct {
+       metaService *meta.MetaService
+}
+
+func NewMetaController(
+       metaService *meta.MetaService,
+) *MetaController {
+       return &MetaController{
+               metaService: metaService,
+       }
+}
+
+// AddOrUpdateReaction add or update reaction
+// @Summary add or update reaction
+// @Description update reaction. if not exist, add one
+// @Tags Meta
+// @Accept json
+// @Produce json
+// @Security ApiKeyAuth
+// @Param data body schema.ReactionReq true "reaction"
+// @Success 200 {object} handler.RespBody
+// @Router /answer/api/v1/meta/reaction [put]
+func (mc *MetaController) AddOrUpdateReaction(ctx *gin.Context) {
+       req := &schema.ReactionReq{}
+       if handler.BindAndCheck(ctx, req) {
+               return
+       }
+       req.UserID = middleware.GetLoginUserIDFromContext(ctx)
+
+       resp, err := mc.metaService.AddOrUpdateReaction(ctx, req)
+       handler.HandleResponse(ctx, err, resp)
+}
diff --git a/internal/entity/meta_entity.go b/internal/entity/meta_entity.go
index df6d6536..203e83c5 100644
--- a/internal/entity/meta_entity.go
+++ b/internal/entity/meta_entity.go
@@ -26,6 +26,7 @@ const (
        QuestionCloseReasonKey = "question.close.reason"
        AnswerEditSummaryKey   = "answer.edit.summary"
        TagEditSummaryKey      = "tag.edit.summary"
+       ObjectReactSummaryKey  = "object.react.summary"
 )
 
 // Meta meta
diff --git a/internal/router/answer_api_router.go 
b/internal/router/answer_api_router.go
index 40a5bba3..a23ddfbf 100644
--- a/internal/router/answer_api_router.go
+++ b/internal/router/answer_api_router.go
@@ -54,6 +54,7 @@ type AnswerAPIRouter struct {
        permissionController    *controller.PermissionController
        userPluginController    *controller.UserPluginController
        reviewController        *controller.ReviewController
+       metaController          *controller.MetaController
 }
 
 func NewAnswerAPIRouter(
@@ -84,6 +85,7 @@ func NewAnswerAPIRouter(
        permissionController *controller.PermissionController,
        userPluginController *controller.UserPluginController,
        reviewController *controller.ReviewController,
+       metaController *controller.MetaController,
 ) *AnswerAPIRouter {
        return &AnswerAPIRouter{
                langController:          langController,
@@ -113,6 +115,7 @@ func NewAnswerAPIRouter(
                permissionController:    permissionController,
                userPluginController:    userPluginController,
                reviewController:        reviewController,
+               metaController:          metaController,
        }
 }
 
@@ -285,6 +288,9 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r 
*gin.RouterGroup) {
        r.GET("/user/plugin/configs", a.userPluginController.GetUserPluginList)
        r.GET("/user/plugin/config", a.userPluginController.GetUserPluginConfig)
        r.PUT("/user/plugin/config", 
a.userPluginController.UpdatePluginUserConfig)
+
+       // meta
+       r.PUT("/meta/reaction", a.metaController.AddOrUpdateReaction)
 }
 
 func (a *AnswerAPIRouter) RegisterAnswerAdminAPIRouter(r *gin.RouterGroup) {
diff --git a/ui/src/pages/Questions/Detail/index.scss 
b/internal/schema/meta_schema.go
similarity index 67%
copy from ui/src/pages/Questions/Detail/index.scss
copy to internal/schema/meta_schema.go
index b2327d79..55fc66e7 100644
--- a/ui/src/pages/Questions/Detail/index.scss
+++ b/internal/schema/meta_schema.go
@@ -17,14 +17,15 @@
  * under the License.
  */
 
-.answer-item {
-  border-top: 1px solid var(--an-answer-item-border-top);
-}
+package schema
 
-@media screen and (max-width: 768px) {
-  .questionDetailPage {
-    h1.h3 {
-      font-size: calc(1.275rem + .3vw)!important;
-    }
-  }
+type ReactionReq struct {
+       ObjectID string `validate:"required" form:"object_id" json:"object_id"` 
// object id
+       Emoji    string `validate:"required" form:"emoji" json:"emoji"`         
// emoji
+       Type     string `validate:"required" form:"type" json:"type"`           
// type
+       UserID   string `json:"-"`
 }
+
+type ReactSummaryMeta map[string][]string
+
+type ReactionResp ReactSummaryMeta
diff --git a/internal/service/activity/activity.go 
b/internal/service/activity/activity.go
index 0f3f8393..f6d692f5 100644
--- a/internal/service/activity/activity.go
+++ b/internal/service/activity/activity.go
@@ -23,9 +23,10 @@ import (
        "context"
        "encoding/json"
        "fmt"
-       "github.com/apache/incubator-answer/internal/service/activity_common"
        "strings"
 
+       "github.com/apache/incubator-answer/internal/service/activity_common"
+
        "github.com/apache/incubator-answer/internal/base/constant"
        "github.com/apache/incubator-answer/internal/base/handler"
        "github.com/apache/incubator-answer/internal/entity"
diff --git a/internal/service/meta/meta_service.go 
b/internal/service/meta/meta_service.go
index 3c229858..24e852cb 100644
--- a/internal/service/meta/meta_service.go
+++ b/internal/service/meta/meta_service.go
@@ -21,10 +21,14 @@ package meta
 
 import (
        "context"
+       "encoding/json"
+       "errors"
 
        "github.com/apache/incubator-answer/internal/base/reason"
        "github.com/apache/incubator-answer/internal/entity"
-       "github.com/segmentfault/pacman/errors"
+       "github.com/apache/incubator-answer/internal/schema"
+       usercommon 
"github.com/apache/incubator-answer/internal/service/user_common"
+       myErrors "github.com/segmentfault/pacman/errors"
 )
 
 // MetaRepo meta repository
@@ -38,12 +42,14 @@ type MetaRepo interface {
 
 // MetaService user service
 type MetaService struct {
-       metaRepo MetaRepo
+       metaRepo   MetaRepo
+       userCommon *usercommon.UserCommon
 }
 
-func NewMetaService(metaRepo MetaRepo) *MetaService {
+func NewMetaService(metaRepo MetaRepo, userCommon *usercommon.UserCommon) 
*MetaService {
        return &MetaService{
-               metaRepo: metaRepo,
+               metaRepo:   metaRepo,
+               userCommon: userCommon,
        }
 }
 
@@ -79,7 +85,7 @@ func (ms *MetaService) GetMetaByObjectIdAndKey(ctx 
context.Context, objectID, ke
                return
        }
        if !exist {
-               return nil, errors.BadRequest(reason.UnknownError)
+               return nil, myErrors.BadRequest(reason.MetaObjectNotFound)
        }
        return meta, nil
 }
@@ -92,3 +98,136 @@ func (ms *MetaService) GetMetaList(ctx context.Context, 
objID string) (metas []*
        }
        return metas, err
 }
+
+// GetReactionByObjectId get reaction
+func (ms *MetaService) GetReactionByObjectId(ctx context.Context, objectID 
string) (resp schema.ReactionResp, err error) {
+       reactionMeta, err := ms.GetMetaByObjectIdAndKey(ctx, objectID, 
entity.ObjectReactSummaryKey)
+
+       // if not exist, return nil
+       if err != nil {
+               var pacmanErr *myErrors.Error
+               if errors.As(err, &pacmanErr) && pacmanErr.Reason == 
reason.MetaObjectNotFound {
+                       return nil, nil
+               } else {
+                       return nil, err
+               }
+       }
+
+       var reaction schema.ReactSummaryMeta
+       err = json.Unmarshal([]byte(reactionMeta.Value), &reaction)
+       if err != nil {
+               return nil, err
+       }
+       return ms.convertToReactionResp(ctx, reaction)
+}
+
+// AddOrUpdateReaction add or update reaction
+func (ms *MetaService) AddOrUpdateReaction(ctx context.Context, req 
*schema.ReactionReq) (resp schema.ReactionResp, err error) {
+       // get reaction for this object
+       reactionMeta, err := ms.GetMetaByObjectIdAndKey(ctx, req.ObjectID, 
entity.ObjectReactSummaryKey)
+
+       var reaction schema.ReactSummaryMeta
+       if err != nil {
+               var pacmanErr *myErrors.Error
+               if errors.As(err, &pacmanErr) && pacmanErr.Reason == 
reason.MetaObjectNotFound {
+                       // create new reaction summary
+                       reaction = schema.ReactSummaryMeta{}
+               } else {
+                       return nil, err
+               }
+       } else {
+               // json unmarshal reactionMeta.Value to reaction
+               err = json.Unmarshal([]byte(reactionMeta.Value), &reaction)
+               if err != nil {
+                       return nil, err
+               }
+       }
+
+       // update reaction
+       ms.updateReaction(req, reaction)
+
+       // write back to meta repo
+       reactSumBytes, err := json.Marshal(reaction)
+       if err != nil {
+               return nil, err
+       }
+
+       if reactionMeta == nil {
+               err = ms.AddMeta(ctx, req.ObjectID, 
entity.ObjectReactSummaryKey, string(reactSumBytes))
+       } else {
+               err = ms.UpdateMeta(ctx, reactionMeta.ID, 
entity.ObjectReactSummaryKey, string(reactSumBytes))
+       }
+
+       if err != nil {
+               return nil, err
+       }
+
+       resp, err = ms.convertToReactionResp(ctx, reaction)
+       if err != nil {
+               return nil, err
+       }
+
+       return resp, nil
+}
+
+// updateReaction update reaction
+func (ms *MetaService) updateReaction(req *schema.ReactionReq, reaction 
schema.ReactSummaryMeta) {
+       emojiUserIds, ok := reaction[req.Emoji]
+
+       if !ok {
+               emojiUserIds = make([]string, 0)
+       }
+
+       found := false
+       for _, item := range emojiUserIds {
+               if item == req.UserID {
+                       found = true
+                       break
+               }
+       }
+
+       removeItem := func(arr []string, target string) []string {
+               result := make([]string, 0, len(arr))
+
+               for _, item := range arr {
+                       if item != target {
+                               result = append(result, item)
+                       }
+               }
+
+               return result
+       }
+
+       if req.Type == "activate" && !found {
+               emojiUserIds = append(emojiUserIds, req.UserID)
+       } else if req.Type == "deactivate" && found {
+               emojiUserIds = removeItem(emojiUserIds, req.UserID)
+       } else if req.Type == "toggle" {
+               if found {
+                       emojiUserIds = removeItem(emojiUserIds, req.UserID)
+               } else {
+                       emojiUserIds = append(emojiUserIds, req.UserID)
+               }
+       }
+
+       reaction[req.Emoji] = emojiUserIds
+}
+
+func (ms *MetaService) convertToReactionResp(ctx context.Context, reaction 
schema.ReactSummaryMeta) (schema.ReactionResp, error) {
+       resp := schema.ReactionResp{}
+       // traverse map and convert to user name
+       for emoji, userIds := range reaction {
+               userNames := make([]string, 0)
+               userBasicInfos, err := 
ms.userCommon.BatchUserBasicInfoByID(ctx, userIds)
+               if err != nil {
+                       return nil, err
+               }
+               // get user name
+               for _, userBasicInfo := range userBasicInfos {
+                       userNames = append(userNames, userBasicInfo.Username)
+               }
+               resp[emoji] = userNames
+       }
+
+       return resp, nil
+}
diff --git a/ui/src/pages/Questions/Detail/components/Answer/index.tsx 
b/ui/src/pages/Questions/Detail/components/Answer/index.tsx
index 474c83d6..6b382dba 100644
--- a/ui/src/pages/Questions/Detail/components/Answer/index.tsx
+++ b/ui/src/pages/Questions/Detail/components/Answer/index.tsx
@@ -36,6 +36,7 @@ import { scrollToElementTop, bgFadeOut } from '@/utils';
 import { AnswerItem } from '@/common/interface';
 import { acceptanceAnswer } from '@/services';
 import { useRenderHtmlPlugin } from '@/utils/pluginKit';
+import Reactions from '../Reactions';
 
 interface Props {
   data: AnswerItem;
@@ -198,6 +199,8 @@ const Index: FC<Props> = ({
         </div>
       </div>
 
+      <Reactions />
+
       <Comment
         objectId={data.id}
         mode="answer"
diff --git a/ui/src/pages/Questions/Detail/components/Question/index.tsx 
b/ui/src/pages/Questions/Detail/components/Question/index.tsx
index 0a76c2f9..1252098a 100644
--- a/ui/src/pages/Questions/Detail/components/Question/index.tsx
+++ b/ui/src/pages/Questions/Detail/components/Question/index.tsx
@@ -37,6 +37,7 @@ import { useRenderHtmlPlugin } from '@/utils/pluginKit';
 import { formatCount, guard } from '@/utils';
 import { following } from '@/services';
 import { pathFactory } from '@/router/pathFactory';
+import Reactions from '../Reactions';
 
 interface Props {
   data: any;
@@ -212,6 +213,8 @@ const Index: FC<Props> = ({ data, initPage, hasAnswer, 
isLogged }) => {
         </div>
       </div>
 
+      <Reactions />
+
       <Comment
         objectId={data?.id}
         mode="question"
diff --git a/ui/src/pages/Questions/Detail/components/Reactions/index.tsx 
b/ui/src/pages/Questions/Detail/components/Reactions/index.tsx
new file mode 100644
index 00000000..6236c684
--- /dev/null
+++ b/ui/src/pages/Questions/Detail/components/Reactions/index.tsx
@@ -0,0 +1,86 @@
+import { FC, memo } from 'react';
+import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap';
+
+import { Icon } from '@/components';
+
+interface Props {}
+
+interface Data {
+  title: string;
+  icon: string;
+  className: string;
+}
+
+const data: Data[] = [
+  { title: 'hello', icon: 'heart-fill', className: 'text-danger' },
+  { title: 'hello', icon: 'emoji-laughing-fill', className: 'text-warning' },
+  { title: 'hello', icon: 'emoji-frown-fill', className: 'text-warning' },
+];
+
+const Index: FC<Props> = () => {
+  const renderTooltip = (props) => (
+    <Tooltip id="reaction-button-tooltip" {...props} bsPrefix="tooltip">
+      <div className="d-block d-md-flex flex-wrap m-0 p-0">
+        {data.map((d) => (
+          <Button
+            key={d.icon}
+            variant="light"
+            size="sm"
+            onClick={() => alert('hellob')}>
+            <Icon name={d.icon} className={d.className} />
+          </Button>
+        ))}
+      </div>
+    </Tooltip>
+  );
+
+  return (
+    <div className="d-block d-md-flex flex-wrap mt-4 mb-3">
+      <Button
+        variant="outline-secondary"
+        className="rounded-pill answer-reaction-btn"
+        size="sm">
+        <Icon name="chat-text-fill" />
+        <span className="ms-1">{6} comments</span>
+      </Button>
+
+      <OverlayTrigger trigger="click" placement="top" overlay={renderTooltip}>
+        <Button
+          variant="outline-secondary"
+          size="sm"
+          className="rounded-pill ms-2 answer-reaction-btn">
+          <Icon name="emoji-smile-fill" />
+          <span className="ms-1">+</span>
+        </Button>
+      </OverlayTrigger>
+
+      {/* <div className="arrow" /> */}
+
+      {data.map((d) => (
+        <OverlayTrigger
+          placement="top"
+          // trigger="hover"
+          trigger="click"
+          overlay={
+            <Tooltip>
+              <div className="text-start">
+                <b>heart</b> <br /> fenbox, joyqi, robin, andrus, jackathon and
+                7 more...
+              </div>
+            </Tooltip>
+          }>
+          <Button
+            title="hahah"
+            variant="outline-secondary"
+            className="rounded-pill ms-2 answer-reaction-btn"
+            size="sm">
+            <Icon name={d.icon} className={d.className} />
+            <span className="ms-1">{3}</span>
+          </Button>
+        </OverlayTrigger>
+      ))}
+    </div>
+  );
+};
+
+export default memo(Index);
diff --git a/ui/src/pages/Questions/Detail/index.scss 
b/ui/src/pages/Questions/Detail/index.scss
index b2327d79..cf5e910d 100644
--- a/ui/src/pages/Questions/Detail/index.scss
+++ b/ui/src/pages/Questions/Detail/index.scss
@@ -16,15 +16,53 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+@import 'bootstrap/scss/functions';
+@import 'bootstrap/scss/variables';
 
-.answer-item {
-  border-top: 1px solid var(--an-answer-item-border-top);
+.answer-reaction-btn {
+  padding-top: 0.2rem;
+  padding-bottom: 0.2rem;
+  border: 1px solid $gray-300;
+}
+
+#reaction-button-tooltip {
+  .tooltip-inner {
+    padding: 0.2rem;
+    background-color: $white;
+    border: 1px solid $gray-300;
+    .btn {
+      background-color: $white;
+      border: none;
+      &:hover {
+        background-color: $gray-400;
+      }
+    }
+  }
+
+  .tooltip-arrow {
+    overflow: hidden;
+    bottom: calc(-1 * $tooltip-arrow-height + 1px);
+    // width: calc($tooltip-arrow-width + 4px);
+    // height: calc($tooltip-arrow-height + 2px);
+    // box-sizing: content-box;
+    &::before {
+      border: 1px solid $gray-300;
+      width: 50%;
+      height: 100%;
+      transform: translate(-50%, -50%) rotate(-45deg);
+      // transform: rotate(-45deg);
+      box-sizing: content-box;
+      left: 50%;
+      bottom: 50%;
+      background-color: white;
+    }
+  }
 }
 
 @media screen and (max-width: 768px) {
   .questionDetailPage {
     h1.h3 {
-      font-size: calc(1.275rem + .3vw)!important;
+      font-size: calc(1.275rem + 0.3vw) !important;
     }
   }
 }

Reply via email to